| /* |
| * |
| * 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 |
| * Implementation for the Weave Device Layer TraitManager object. |
| * |
| */ |
| |
| |
| #include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h> |
| |
| #if WEAVE_DEVICE_CONFIG_ENABLE_TRAIT_MANAGER |
| |
| #include <Weave/DeviceLayer/TraitManager.h> |
| #include <Weave/Profiles/security/ApplicationKeysTraitDataSink.h> |
| #include <Weave/DeviceLayer/internal/DeviceIdentityTraitDataSource.h> |
| #include <Weave/DeviceLayer/internal/LocaleSettingsTraitDataSink.h> |
| |
| using namespace ::nl::Weave; |
| using namespace ::nl::Weave::DeviceLayer; |
| using namespace ::nl::Weave::DeviceLayer::Internal; |
| using namespace ::nl::Weave::Profiles::DataManagement_Current; |
| |
| using Schema::Weave::Trait::Auth::ApplicationKeysTrait::ApplicationKeysTraitDataSink; |
| |
| namespace { |
| |
| template <typename T> |
| class TraitCatalogImpl : public TraitCatalogBase<T> |
| { |
| public: |
| enum |
| { |
| kMaxEntries = 20 |
| }; |
| |
| TraitCatalogImpl(); |
| virtual ~TraitCatalogImpl(); |
| |
| WEAVE_ERROR Add(const ResourceIdentifier & resId, const uint64_t & instanceId, PropertyPathHandle basePathHandle, T * traitInstance, TraitDataHandle & traitHandle); |
| WEAVE_ERROR Remove(T * traitInstance); |
| WEAVE_ERROR PrepareSubscriptionPathList(TraitPath * pathList, uint16_t pathListSize, uint16_t & pathListLen); |
| |
| virtual WEAVE_ERROR AddressToHandle(TLV::TLVReader & aReader, TraitDataHandle & aHandle, |
| SchemaVersionRange & aSchemaVersionRange) const; |
| virtual WEAVE_ERROR HandleToAddress(TraitDataHandle aHandle, TLV::TLVWriter & aWriter, |
| SchemaVersionRange & aSchemaVersionRange) const; |
| virtual WEAVE_ERROR Locate(TraitDataHandle aHandle, T ** aTraitInstance) const; |
| virtual WEAVE_ERROR Locate(T * aTraitInstance, TraitDataHandle & aHandle) const; |
| virtual WEAVE_ERROR DispatchEvent(uint16_t aEvent, void * aContext) const; |
| virtual void Iterate(IteratorCallback aCallback, void * aContext); |
| #if WEAVE_CONFIG_ENABLE_WDM_UPDATE |
| virtual WEAVE_ERROR GetInstanceId(TraitDataHandle aHandle, uint64_t &aInstanceId) const; |
| virtual WEAVE_ERROR GetResourceId(TraitDataHandle aHandle, ResourceIdentifier &aResourceId) const; |
| #endif // WEAVE_CONFIG_ENABLE_WDM_UPDATE |
| |
| private: |
| |
| struct CatalogEntry |
| { |
| ResourceIdentifier ResourceId; |
| uint64_t InstanceId; |
| T * Item; |
| PropertyPathHandle BasePathHandle; |
| uint8_t EntryRevision; |
| }; |
| |
| CatalogEntry mEntries[kMaxEntries]; |
| |
| static inline uint8_t HandleIndex(TraitDataHandle handle) { return (uint8_t)handle; } |
| static inline uint8_t HandleRevision(TraitDataHandle handle) { return (uint8_t)(handle >> 8); } |
| static inline TraitDataHandle MakeTraitDataHandle(uint8_t index, uint8_t revision) { return ((TraitDataHandle)revision) << 8 | index; } |
| }; |
| |
| typedef TraitCatalogImpl<TraitDataSink> TraitSinkCatalog; |
| typedef TraitCatalogImpl<TraitDataSource> TraitSourceCatalog; |
| |
| SubscriptionEngine WdmSubscriptionEngine; |
| TraitSinkCatalog SubscribedServiceTraits; |
| TraitSourceCatalog PublishedTraits; |
| #if !defined(__Fuchsia__) |
| ApplicationKeysTraitDataSink AppKeysTraitDataSink; |
| DeviceIdentityTraitDataSource DeviceIdTraitDataSource; |
| #else |
| LocaleSettingsTraitDataSink LocaleSettings; |
| #endif |
| } // unnamed namespace |
| |
| namespace nl { |
| namespace Weave { |
| namespace DeviceLayer { |
| |
| TraitManager TraitManager::sInstance; |
| |
| WEAVE_ERROR TraitManager::SetServiceSubscriptionMode(ServiceSubscriptionMode val) |
| { |
| mServiceSubMode = val; |
| DriveServiceSubscriptionState(false); |
| return WEAVE_NO_ERROR; |
| } |
| |
| uint32_t TraitManager::GetServiceSubscribeConfirmIntervalMS(void) const |
| { |
| // TODO: implement me |
| return 0; |
| } |
| |
| WEAVE_ERROR TraitManager::SetServiceSubscribeConfirmIntervalMS(uint32_t val) const |
| { |
| // TODO: implement me |
| return WEAVE_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| WEAVE_ERROR TraitManager::SubscribeServiceTrait(const ResourceIdentifier & resId, const uint64_t & instanceId, |
| PropertyPathHandle basePathHandle, TraitDataSink * dataSink) |
| { |
| TraitDataHandle traitHandle; |
| return SubscribedServiceTraits.Add(resId, instanceId, basePathHandle, dataSink, traitHandle); |
| } |
| |
| WEAVE_ERROR TraitManager::UnsubscribeServiceTrait(TraitDataSink * dataSink) |
| { |
| return SubscribedServiceTraits.Remove(dataSink); |
| } |
| |
| WEAVE_ERROR TraitManager::PublishTrait(const uint64_t & instanceId, TraitDataSource * dataSource) |
| { |
| ResourceIdentifier selfResId(ResourceIdentifier::RESOURCE_TYPE_RESERVED, ResourceIdentifier::SELF_NODE_ID); |
| TraitDataHandle traitHandle; |
| return PublishedTraits.Add(selfResId, instanceId, kRootPropertyPathHandle, dataSource, traitHandle); |
| } |
| |
| WEAVE_ERROR TraitManager::PublishTrait(const ResourceIdentifier & resId, const uint64_t & instanceId, TraitDataSource * dataSource) |
| { |
| TraitDataHandle traitHandle; |
| return PublishedTraits.Add(resId, instanceId, kRootPropertyPathHandle, dataSource, traitHandle); |
| } |
| |
| WEAVE_ERROR TraitManager::UnpublishTrait(TraitDataSource * dataSource) |
| { |
| return PublishedTraits.Remove(dataSource); |
| } |
| |
| WEAVE_ERROR TraitManager::Init(void) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| Binding * serviceBinding = NULL; |
| |
| err = WdmSubscriptionEngine.Init(&ExchangeMgr, NULL, HandleSubscriptionEngineEvent); |
| SuccessOrExit(err); |
| |
| serviceBinding = ExchangeMgr.NewBinding(HandleServiceBindingEvent, this); |
| VerifyOrExit(NULL != serviceBinding, err = WEAVE_ERROR_NO_MEMORY); |
| |
| err = SubscriptionEngine::GetInstance()->NewClient(&mServiceSubClient, |
| serviceBinding, |
| this, |
| HandleOutboundServiceSubscriptionEvent, |
| &SubscribedServiceTraits, |
| 5000); // TODO: set default subscribe response timeout properly this |
| SuccessOrExit(err); |
| |
| serviceBinding->Release(); |
| |
| mServiceSubClient->EnableResubscribe(NULL); |
| |
| err = SubscriptionEngine::GetInstance()->EnablePublisher(NULL, &PublishedTraits); |
| SuccessOrExit(err); |
| |
| #if !defined(__Fuchsia__) |
| AppKeysTraitDataSink.SetGroupKeyStore(ConfigurationMgr().GetGroupKeyStore()); |
| |
| { |
| ResourceIdentifier resourceId(ResourceIdentifier::RESOURCE_TYPE_RESERVED, ResourceIdentifier::SELF_NODE_ID); |
| TraitDataHandle handle; |
| err = SubscribedServiceTraits.Add(resourceId, 0, kRootPropertyPathHandle, &AppKeysTraitDataSink, handle); |
| SuccessOrExit(err); |
| err = PublishedTraits.Add(resourceId, 0, kRootPropertyPathHandle, &DeviceIdTraitDataSource, handle); |
| SuccessOrExit(err); |
| } |
| #else |
| { |
| TraitDataHandle handle; |
| err = SubscribedServiceTraits.Add(nl::Weave::FabricState.LocalNodeId, 0, kRootPropertyPathHandle, &LocaleSettings, handle); |
| SuccessOrExit(err); |
| } |
| #endif |
| |
| mServiceSubMode = kServiceSubscriptionMode_Enabled; |
| mServicePathList = NULL; |
| mServiceCounterSubHandler = NULL; |
| mFlags = 0; |
| |
| exit: |
| return err; |
| } |
| |
| void TraitManager::OnPlatformEvent(const WeaveDeviceEvent* event) |
| { |
| // If connectivity to the service has changed... |
| if (event->Type == DeviceEventType::kServiceConnectivityChange) |
| { |
| // Update the service subscription state as needed. |
| DriveServiceSubscriptionState(true); |
| } |
| } |
| |
| void TraitManager::DriveServiceSubscriptionState(bool serviceConnectivityChanged) |
| { |
| bool serviceSubShouldBeActivated = |
| (mServiceSubMode == kServiceSubscriptionMode_Enabled && |
| ConnectivityMgr().IsWiFiStationProvisioned() && |
| ConfigurationMgr().IsPairedToAccount()); |
| |
| // If the service subscription activation state needs to change... |
| if (GetFlag(mFlags, kFlag_ServiceSubscriptionActivated) != serviceSubShouldBeActivated) |
| { |
| // If the system currently has service connectivity... |
| if (ConnectivityMgr().HaveServiceConnectivity()) |
| { |
| // Update the activation state. |
| SetFlag(mFlags, kFlag_ServiceSubscriptionActivated, serviceSubShouldBeActivated); |
| |
| // If service subscription should be activated, schedule an async work item to activate it. |
| if (serviceSubShouldBeActivated) |
| { |
| PlatformMgr().ScheduleWork(ActivateServiceSubscription); |
| } |
| |
| // If the service subscription should be deactivated... |
| else |
| { |
| // Abort both the outgoing and incoming service subscriptions, if established. |
| mServiceSubClient->AbortSubscription(); |
| if (mServiceCounterSubHandler != NULL) |
| { |
| mServiceCounterSubHandler->AbortSubscription(); |
| mServiceCounterSubHandler = NULL; |
| } |
| |
| // If prior to this the service subscription was fully established (including the service's |
| // counter subscription) change the state and raise an event announcing the loss of the |
| // subscription. |
| if (GetFlag(sInstance.mFlags, kFlag_ServiceSubscriptionEstablished)) |
| { |
| ClearFlag(sInstance.mFlags, kFlag_ServiceSubscriptionEstablished); |
| WeaveDeviceEvent event; |
| event.Type = DeviceEventType::kServiceSubscriptionStateChange; |
| event.ServiceSubscriptionStateChange.Result = kConnectivity_Lost; |
| PlatformMgr().PostEvent(&event); |
| } |
| } |
| } |
| } |
| |
| // Otherwise, if service connectivity has just been established, and a service subscription should |
| // be active, but currently isn't, kick-start the resubscription process. |
| else if (serviceConnectivityChanged && ConnectivityMgr().HaveServiceConnectivity() && |
| serviceSubShouldBeActivated && !mServiceSubClient->IsInProgressOrEstablished()) |
| { |
| mServiceSubClient->ResetResubscribe(); |
| } |
| } |
| |
| void TraitManager::ActivateServiceSubscription(intptr_t arg) |
| { |
| // Enable automatic resubscription to the service using the default resubscription back-off policy. |
| sInstance.mServiceSubClient->EnableResubscribe(NULL); |
| |
| // Initial the outbound service subscription. This will ultimately result in the service |
| // setting up an inbound counter-subscription back to the device, at which point the |
| // full mutual service subscription is considered established. |
| sInstance.mServiceSubClient->InitiateSubscription(); |
| } |
| |
| void TraitManager::HandleSubscriptionEngineEvent(void * appState, SubscriptionEngine::EventID eventType, |
| const SubscriptionEngine::InEventParam & inParam, SubscriptionEngine::OutEventParam & outParam) |
| { |
| switch (eventType) |
| { |
| case SubscriptionEngine::kEvent_OnIncomingSubscribeRequest: |
| outParam.mIncomingSubscribeRequest.mHandlerEventCallback = HandleInboundSubscriptionEvent; |
| outParam.mIncomingSubscribeRequest.mHandlerAppState = NULL; |
| outParam.mIncomingSubscribeRequest.mRejectRequest = false; |
| // TODO: Is this necessary? Why isn't the counter subscription using the same Binding as the client subscription? |
| // inParam.mIncomingSubscribeRequest.mBinding->SetDefaultResponseTimeout(kResponseTimeoutMsec); |
| // inParam.mIncomingSubscribeRequest.mBinding->SetDefaultWRMPConfig(gWRMPConfig); |
| break; |
| |
| // TODO: Add support for subscriptionless notifies |
| |
| default: |
| SubscriptionEngine::DefaultEventHandler(eventType, inParam, outParam); |
| break; |
| } |
| } |
| |
| void TraitManager::HandleServiceBindingEvent(void * appState, ::nl::Weave::Binding::EventType eventType, |
| const ::nl::Weave::Binding::InEventParam & inParam, ::nl::Weave::Binding::OutEventParam & outParam) |
| { |
| Binding * binding = inParam.Source; |
| |
| // TODO: fix logging |
| switch (eventType) |
| { |
| case nl::Weave::Binding::kEvent_PrepareRequested: |
| // TODO: set response timeout |
| // TODO: set WRM config |
| outParam.PrepareRequested.PrepareError = binding->BeginConfiguration() |
| .Target_ServiceEndpoint(kServiceEndpoint_Data_Management) |
| .Transport_UDP_WRM() |
| .Security_SharedCASESession() |
| .PrepareBinding(); |
| break; |
| |
| case nl::Weave::Binding::kEvent_PrepareFailed: |
| WeaveLogProgress(DeviceLayer, "Failed to prepare service subscription binding: %s", ErrorStr(inParam.PrepareFailed.Reason)); |
| break; |
| |
| case nl::Weave::Binding::kEvent_BindingFailed: |
| WeaveLogProgress(DeviceLayer, "Service subscription binding failed: %s", ErrorStr(inParam.BindingFailed.Reason)); |
| break; |
| |
| case nl::Weave::Binding::kEvent_BindingReady: |
| WeaveLogProgress(DeviceLayer, "Service subscription binding ready"); |
| break; |
| |
| default: |
| nl::Weave::Binding::DefaultEventHandler(appState, eventType, inParam, outParam); |
| } |
| } |
| |
| void TraitManager::HandleOutboundServiceSubscriptionEvent(void * appState, SubscriptionClient::EventID eventType, |
| const SubscriptionClient::InEventParam & inParam, SubscriptionClient::OutEventParam & outParam) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| TraitManager* self = reinterpret_cast<TraitManager*>(appState); |
| |
| switch (eventType) |
| { |
| case SubscriptionClient::kEvent_OnSubscribeRequestPrepareNeeded: |
| { |
| uint16_t pathListLen; |
| |
| // TODO: Fix *LAME* SubscriptionClient callback API to handle errors!!!! |
| // TODO: Fix *LAME* SubscriptionClient callback API to support initiator versions!!!! |
| |
| if (sInstance.mServicePathList == NULL) |
| { |
| sInstance.mServicePathList = new TraitPath[TraitSinkCatalog::kMaxEntries]; |
| } |
| |
| err = SubscribedServiceTraits.PrepareSubscriptionPathList(sInstance.mServicePathList, TraitSinkCatalog::kMaxEntries, pathListLen); |
| SuccessOrExit(err); |
| |
| outParam.mSubscribeRequestPrepareNeeded.mPathList = sInstance.mServicePathList; |
| outParam.mSubscribeRequestPrepareNeeded.mPathListSize = pathListLen; |
| outParam.mSubscribeRequestPrepareNeeded.mVersionedPathList = NULL; |
| outParam.mSubscribeRequestPrepareNeeded.mNeedAllEvents = false; |
| outParam.mSubscribeRequestPrepareNeeded.mLastObservedEventList = NULL; |
| outParam.mSubscribeRequestPrepareNeeded.mLastObservedEventListSize = 0; |
| outParam.mSubscribeRequestPrepareNeeded.mTimeoutSecMin = 30; // TODO: set these properly |
| outParam.mSubscribeRequestPrepareNeeded.mTimeoutSecMax = 60; |
| |
| WeaveLogProgress(DeviceLayer, "Sending outbound service subscribe request (path count %" PRIu16 ")", pathListLen); |
| |
| break; |
| } |
| case SubscriptionClient::kEvent_OnSubscriptionEstablished: |
| WeaveLogProgress(DeviceLayer, "Outbound service subscription established (sub id %016" PRIX64 ")", |
| inParam.mSubscriptionEstablished.mSubscriptionId); |
| break; |
| |
| case SubscriptionClient::kEvent_OnSubscriptionTerminated: |
| WeaveLogProgress(DeviceLayer, "Outbound service subscription terminated: %s", |
| (inParam.mSubscriptionTerminated.mIsStatusCodeValid) |
| ? StatusReportStr(inParam.mSubscriptionTerminated.mStatusProfileId, inParam.mSubscriptionTerminated.mStatusCode) |
| : ErrorStr(inParam.mSubscriptionTerminated.mReason)); |
| // If prior to this the service subscription was fully established (including the service's counter subscription) |
| // change the state and raise an event announcing the loss of the subscription. |
| if (GetFlag(sInstance.mFlags, kFlag_ServiceSubscriptionEstablished)) |
| { |
| ClearFlag(sInstance.mFlags, kFlag_ServiceSubscriptionEstablished); |
| WeaveDeviceEvent event; |
| event.Type = DeviceEventType::kServiceSubscriptionStateChange; |
| event.ServiceSubscriptionStateChange.Result = kConnectivity_Lost; |
| PlatformMgr().PostEvent(&event); |
| } |
| // Disable resubscribe as subscription was already terminated. |
| if (!ConfigurationMgr().IsPairedToAccount()) { |
| self->mServiceSubClient->DisableResubscribe(); |
| SetFlag(self->mFlags, kFlag_ServiceSubscriptionActivated, false); |
| } |
| break; |
| |
| default: |
| SubscriptionClient::DefaultEventHandler(eventType, inParam, outParam); |
| break; |
| } |
| |
| exit: |
| return; |
| } |
| |
| void TraitManager::HandleInboundSubscriptionEvent(void * aAppState, SubscriptionHandler::EventID eventType, |
| const SubscriptionHandler::InEventParam & inParam, SubscriptionHandler::OutEventParam & outParam) |
| { |
| switch (eventType) |
| { |
| case SubscriptionHandler::kEvent_OnSubscribeRequestParsed: |
| if (inParam.mSubscribeRequestParsed.mIsSubscriptionIdValid && |
| inParam.mSubscribeRequestParsed.mMsgInfo->SourceNodeId == kServiceEndpoint_Data_Management) |
| { |
| WeaveLogProgress(DeviceLayer, "Inbound service counter-subscription request received (sub id %016" PRIX64 ", path count %" PRId16 ")", |
| inParam.mSubscribeRequestParsed.mSubscriptionId, |
| inParam.mSubscribeRequestParsed.mNumTraitInstances); |
| |
| sInstance.mServiceCounterSubHandler = inParam.mSubscribeRequestParsed.mHandler; |
| } |
| else |
| { |
| #if WEAVE_PROGRESS_LOGGING |
| char peerDesc[kWeavePeerDescription_MaxLength]; |
| WeaveMessageLayer::GetPeerDescription(peerDesc, sizeof(peerDesc), inParam.mSubscribeRequestParsed.mMsgInfo); |
| WeaveLogProgress(DeviceLayer, "Inbound subscription request received from node %s (path count %" PRId16 ")", |
| peerDesc, |
| inParam.mSubscribeRequestParsed.mNumTraitInstances); |
| #endif // WEAVE_PROGRESS_LOGGING |
| } |
| |
| // TODO: dispatch this to the event loop to avoid crazy-deep stack. |
| // TODO: is this the right way to set the subscription timeout? |
| inParam.mSubscribeRequestParsed.mHandler->AcceptSubscribeRequest(inParam.mSubscribeRequestParsed.mTimeoutSecMin); |
| break; |
| |
| case SubscriptionHandler::kEvent_OnSubscriptionEstablished: |
| if (inParam.mSubscriptionEstablished.mHandler == sInstance.mServiceCounterSubHandler) |
| { |
| WeaveLogProgress(DeviceLayer, "Inbound service counter-subscription established"); |
| |
| // Note that the service subscription is fully established. |
| SetFlag(sInstance.mFlags, kFlag_ServiceSubscriptionEstablished); |
| |
| // Raise an event announcing the establishment of the subscription. |
| { |
| WeaveDeviceEvent event; |
| event.Type = DeviceEventType::kServiceSubscriptionStateChange; |
| event.ServiceSubscriptionStateChange.Result = kConnectivity_Established; |
| PlatformMgr().PostEvent(&event); |
| } |
| } |
| else |
| { |
| #if WEAVE_PROGRESS_LOGGING |
| uint64_t peerNodeId = inParam.mSubscriptionEstablished.mHandler->GetPeerNodeId(); |
| uint64_t subId; |
| inParam.mSubscriptionEstablished.mHandler->GetSubscriptionId(&subId); |
| WeaveLogProgress(DeviceLayer, "Inbound subscription established with node %016" PRIX64 "(sub id %016" PRIX64 ")", |
| peerNodeId, subId); |
| #endif // WEAVE_PROGRESS_LOGGING |
| } |
| break; |
| |
| case SubscriptionHandler::kEvent_OnSubscriptionTerminated: |
| { |
| #if WEAVE_PROGRESS_LOGGING |
| const char * termDesc = |
| (inParam.mSubscriptionTerminated.mReason == WEAVE_ERROR_STATUS_REPORT_RECEIVED) |
| ? StatusReportStr(inParam.mSubscriptionTerminated.mStatusProfileId, inParam.mSubscriptionTerminated.mStatusCode) |
| : ErrorStr(inParam.mSubscriptionTerminated.mReason); |
| #endif // WEAVE_PROGRESS_LOGGING |
| if (inParam.mSubscriptionTerminated.mHandler == sInstance.mServiceCounterSubHandler) |
| { |
| WeaveLogProgress(DeviceLayer, "Inbound service counter-subscription terminated: %s", termDesc); |
| |
| sInstance.mServiceCounterSubHandler = NULL; |
| |
| // If prior to this the service subscription was fully established (including the device's outbound subscription) |
| // change the state and raise an event announcing the loss of the subscription. |
| if (GetFlag(sInstance.mFlags, kFlag_ServiceSubscriptionEstablished)) |
| { |
| ClearFlag(sInstance.mFlags, kFlag_ServiceSubscriptionEstablished); |
| WeaveDeviceEvent event; |
| event.Type = DeviceEventType::kServiceSubscriptionStateChange; |
| event.ServiceSubscriptionStateChange.Result = kConnectivity_Lost; |
| PlatformMgr().PostEvent(&event); |
| } |
| } |
| else |
| { |
| #if WEAVE_PROGRESS_LOGGING |
| uint64_t peerNodeId = inParam.mSubscriptionTerminated.mHandler->GetPeerNodeId(); |
| uint64_t subId; |
| inParam.mSubscriptionTerminated.mHandler->GetSubscriptionId(&subId); |
| WeaveLogProgress(DeviceLayer, "Inbound subscription terminated with node %016" PRIX64 "(sub id %016" PRIX64 "): %s", |
| peerNodeId, subId, termDesc); |
| #endif // WEAVE_PROGRESS_LOGGING |
| } |
| break; |
| } |
| |
| default: |
| SubscriptionHandler::DefaultEventHandler(eventType, inParam, outParam); |
| break; |
| } |
| } |
| |
| |
| } // namespace DeviceLayer |
| } // namespace Weave |
| } // namespace nl |
| |
| |
| namespace { |
| |
| template <typename T> |
| TraitCatalogImpl<T>::TraitCatalogImpl() |
| { |
| // Nothing to do. |
| } |
| |
| template <typename T> |
| TraitCatalogImpl<T>::~TraitCatalogImpl() |
| { |
| // Nothing to do. |
| } |
| |
| template <typename T> |
| WEAVE_ERROR TraitCatalogImpl<T>::Add(const ResourceIdentifier & resId, const uint64_t & instanceId, |
| PropertyPathHandle basePathHandle, T * traitInstance, TraitDataHandle & traitHandle) |
| { |
| uint8_t freeIndex = kMaxEntries; |
| |
| // Search the catalog... |
| for (uint8_t i = 0; i < kMaxEntries; i++) |
| { |
| // Keep track of the first free entry. |
| if (mEntries[i].Item == NULL) |
| { |
| if (freeIndex == kMaxEntries) |
| { |
| freeIndex = i; |
| } |
| continue; |
| } |
| |
| // If the resource, trait id and instance id match an existing entry, replace the |
| // existing trait instance with the supplied one, reusing the assigned trait handle. |
| if (mEntries[i].ResourceId == resId && |
| mEntries[i].Item->GetSchemaEngine()->GetProfileId() == traitInstance->GetSchemaEngine()->GetProfileId() && |
| mEntries[i].InstanceId == instanceId) |
| { |
| mEntries[i].Item = traitInstance; |
| mEntries[i].BasePathHandle = basePathHandle; |
| traitHandle = MakeTraitDataHandle(i, mEntries[i].EntryRevision); |
| return WEAVE_NO_ERROR; |
| } |
| } |
| |
| // Fail if the catalog is full. |
| if (freeIndex == kMaxEntries) |
| { |
| return WEAVE_ERROR_NO_MEMORY; |
| } |
| |
| // Add the new trait instance. |
| mEntries[freeIndex].ResourceId = resId; |
| mEntries[freeIndex].InstanceId = instanceId; |
| mEntries[freeIndex].Item = traitInstance; |
| mEntries[freeIndex].BasePathHandle = basePathHandle; |
| mEntries[freeIndex].EntryRevision++; |
| |
| traitHandle = MakeTraitDataHandle(freeIndex, mEntries[freeIndex].EntryRevision); |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| template <typename T> |
| WEAVE_ERROR TraitCatalogImpl<T>::Remove(T * traitInstance) |
| { |
| for (TraitDataHandle i = 0; i < kMaxEntries; i++) |
| { |
| if (mEntries[i].Item == traitInstance) |
| { |
| mEntries[i].Item = NULL; |
| return WEAVE_NO_ERROR; |
| } |
| } |
| |
| return WEAVE_ERROR_INVALID_ARGUMENT; |
| } |
| |
| template <typename T> |
| WEAVE_ERROR TraitCatalogImpl<T>::PrepareSubscriptionPathList(TraitPath * pathList, uint16_t pathListSize, uint16_t & pathListLen) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| pathListLen = 0; |
| |
| for (uint8_t i = 0; i < kMaxEntries; i++) |
| { |
| if (mEntries[i].Item != NULL) |
| { |
| VerifyOrExit(pathListLen < pathListSize, err = WEAVE_ERROR_BUFFER_TOO_SMALL); |
| *pathList++ = TraitPath(MakeTraitDataHandle(i, mEntries[i].EntryRevision), mEntries[i].BasePathHandle); |
| pathListLen++; |
| } |
| } |
| |
| exit: |
| return err; |
| } |
| |
| WEAVE_ERROR DecodeTraitInstancePath(TLV::TLVReader & aReader, ResourceIdentifier & resourceId, uint32_t & profileId, |
| SchemaVersionRange & aSchemaVersionRange, uint64_t & instanceId) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| Path::Parser path; |
| |
| instanceId = 0; |
| |
| err = path.Init(aReader); |
| SuccessOrExit(err); |
| |
| { |
| nl::Weave::TLV::TLVReader reader; |
| |
| err = path.GetResourceID(&reader); |
| if (err == WEAVE_NO_ERROR) |
| { |
| err = resourceId.FromTLV(reader, ::nl::Weave::DeviceLayer::FabricState.LocalNodeId); |
| SuccessOrExit(err); |
| } |
| else if (err == WEAVE_END_OF_TLV) |
| { |
| resourceId = ResourceIdentifier(ResourceIdentifier::RESOURCE_TYPE_RESERVED, ResourceIdentifier::SELF_NODE_ID); |
| } |
| else |
| { |
| ExitNow(); |
| } |
| } |
| |
| err = path.GetProfileID(&profileId, &aSchemaVersionRange); |
| SuccessOrExit(err); |
| |
| err = path.GetInstanceID(&instanceId); |
| if ((WEAVE_NO_ERROR != err) && (WEAVE_END_OF_TLV != err)) |
| { |
| ExitNow(); |
| } |
| |
| err = path.GetTags(&aReader); |
| SuccessOrExit(err); |
| |
| exit: |
| return err; |
| } |
| |
| template <typename T> |
| WEAVE_ERROR TraitCatalogImpl<T>::AddressToHandle(TLV::TLVReader & aReader, TraitDataHandle & aHandle, |
| SchemaVersionRange & aSchemaVersionRange) const |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| ResourceIdentifier resourceId; |
| uint32_t profileId; |
| uint64_t instanceId = 0; |
| |
| err = DecodeTraitInstancePath(aReader, resourceId, profileId, aSchemaVersionRange, instanceId); |
| SuccessOrExit(err); |
| |
| err = WEAVE_ERROR_INVALID_PROFILE_ID; // TODO: Use sensible error! |
| for (uint8_t i = 0; i < kMaxEntries; i++) |
| { |
| const CatalogEntry * entry = &mEntries[i]; |
| |
| if (entry->Item != NULL && |
| entry->ResourceId == resourceId && |
| entry->Item->GetSchemaEngine()->GetProfileId() == profileId && |
| entry->InstanceId == instanceId) |
| { |
| err = WEAVE_NO_ERROR; |
| aHandle = MakeTraitDataHandle(i, entry->EntryRevision); |
| break; |
| } |
| } |
| SuccessOrExit(err); |
| |
| exit: |
| return err; |
| } |
| |
| WEAVE_ERROR EncodeTraitInstancePath(TLV::TLVWriter & writer, const ResourceIdentifier & resourceId, uint32_t profileId, |
| SchemaVersionRange & schemaVersionRange, const uint64_t & instanceId) |
| { |
| WEAVE_ERROR err; |
| TLV::TLVType containerType; |
| |
| VerifyOrExit(schemaVersionRange.IsValid(), err = WEAVE_ERROR_INVALID_ARGUMENT); |
| |
| err = writer.StartContainer(TLV::ContextTag(Path::kCsTag_InstanceLocator), TLV::kTLVType_Structure, containerType); |
| SuccessOrExit(err); |
| |
| if (schemaVersionRange.mMinVersion != 1 || schemaVersionRange.mMaxVersion != 1) |
| { |
| TLV::TLVType containerType2; |
| |
| err = writer.StartContainer(TLV::ContextTag(Path::kCsTag_TraitProfileID), TLV::kTLVType_Array, containerType2); |
| SuccessOrExit(err); |
| |
| err = writer.Put(TLV::AnonymousTag, profileId); |
| SuccessOrExit(err); |
| |
| // Only encode the max version if it isn't 1. |
| if (schemaVersionRange.mMaxVersion != 1) |
| { |
| err = writer.Put(TLV::AnonymousTag, schemaVersionRange.mMaxVersion); |
| SuccessOrExit(err); |
| } |
| |
| // Only encode the min version if it isn't 1. |
| if (schemaVersionRange.mMinVersion != 1) |
| { |
| err = writer.Put(TLV::AnonymousTag, schemaVersionRange.mMinVersion); |
| SuccessOrExit(err); |
| } |
| |
| err = writer.EndContainer(containerType2); |
| SuccessOrExit(err); |
| } |
| else |
| { |
| err = writer.Put(TLV::ContextTag(Path::kCsTag_TraitProfileID), profileId); |
| SuccessOrExit(err); |
| } |
| |
| if (instanceId != 0) |
| { |
| err = writer.Put(TLV::ContextTag(Path::kCsTag_TraitInstanceID), instanceId); |
| SuccessOrExit(err); |
| } |
| |
| err = resourceId.ToTLV(writer); |
| SuccessOrExit(err); |
| |
| err = writer.EndContainer(containerType); |
| SuccessOrExit(err); |
| |
| exit: |
| return err; |
| } |
| |
| template <typename T> |
| WEAVE_ERROR TraitCatalogImpl<T>::HandleToAddress(TraitDataHandle aHandle, TLV::TLVWriter& aWriter, |
| SchemaVersionRange& aSchemaVersionRange) const |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| const CatalogEntry * entry; |
| uint32_t profileId; |
| uint8_t handleIndex = HandleIndex(aHandle); |
| uint8_t handleRev = HandleRevision(aHandle); |
| |
| VerifyOrExit(handleIndex < kMaxEntries, err = WEAVE_ERROR_INVALID_ARGUMENT); |
| |
| entry = &mEntries[handleIndex]; |
| |
| VerifyOrExit(handleRev == entry->EntryRevision, err = WEAVE_ERROR_INVALID_ARGUMENT); |
| |
| profileId = entry->Item->GetSchemaEngine()->GetProfileId(); |
| |
| err = EncodeTraitInstancePath(aWriter, entry->ResourceId, profileId, aSchemaVersionRange, entry->InstanceId); |
| SuccessOrExit(err); |
| |
| exit: |
| return err; |
| } |
| |
| template <typename T> |
| WEAVE_ERROR TraitCatalogImpl<T>::Locate(TraitDataHandle aHandle, T ** aTraitInstance) const |
| { |
| uint8_t handleIndex = HandleIndex(aHandle); |
| uint8_t handleRev = HandleRevision(aHandle); |
| |
| if (handleIndex < kMaxEntries && mEntries[handleIndex].Item != NULL && mEntries[handleIndex].EntryRevision == handleRev) |
| { |
| *aTraitInstance = mEntries[handleIndex].Item; |
| return WEAVE_NO_ERROR; |
| } |
| |
| return WEAVE_ERROR_INVALID_ARGUMENT; |
| } |
| |
| template <typename T> |
| WEAVE_ERROR TraitCatalogImpl<T>::Locate(T * aTraitInstance, TraitDataHandle & aHandle) const |
| { |
| for (uint8_t i = 0; i < kMaxEntries; i++) |
| { |
| if (mEntries[i].Item == aTraitInstance) |
| { |
| aHandle = MakeTraitDataHandle(i, mEntries[i].EntryRevision); |
| return WEAVE_NO_ERROR; |
| } |
| } |
| |
| return WEAVE_ERROR_INVALID_ARGUMENT; |
| } |
| |
| template <typename T> |
| WEAVE_ERROR TraitCatalogImpl<T>::DispatchEvent(uint16_t aEvent, void * aContext) const |
| { |
| for (uint8_t i = 0; i < kMaxEntries; i++) |
| { |
| if (mEntries[i].Item != NULL) |
| { |
| mEntries[i].Item->OnEvent(aEvent, aContext); |
| } |
| } |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| template <typename T> |
| void TraitCatalogImpl<T>::Iterate(IteratorCallback aCallback, void * aContext) |
| { |
| for (uint8_t i = 0; i < kMaxEntries; i++) |
| { |
| if (mEntries[i].Item != NULL) |
| { |
| aCallback(mEntries[i].Item, MakeTraitDataHandle(i, mEntries[i].EntryRevision), aContext); |
| } |
| } |
| } |
| |
| template class TraitCatalogImpl<TraitDataSink>; |
| template class TraitCatalogImpl<TraitDataSource>; |
| |
| } // unnamed namespace |
| |
| nl::Weave::Profiles::DataManagement::SubscriptionEngine * nl::Weave::Profiles::DataManagement::SubscriptionEngine::GetInstance() |
| { |
| return &WdmSubscriptionEngine; |
| } |
| |
| #else |
| #include <Weave/Profiles/data-management/Current/DataManagement.h> |
| nl::Weave::Profiles::DataManagement::SubscriptionEngine * nl::Weave::Profiles::DataManagement::SubscriptionEngine::GetInstance() { |
| return nullptr; |
| } |
| #endif // WEAVE_DEVICE_CONFIG_ENABLE_TRAIT_MANAGER |