Move DeviceInfoProvider to common code and use it in linux and NRF. (#18687)
* Add and use DeviceInfoProvider for Zephyr. nrfconnect and telink use this
* Restyle
* Make code compile
* Switch new/delete to platform new/delete
* Start moving DeviceInfoProviderImpl into a common example file that all examples can use. Expect other applications to replace these and override
* Add a readme for the directory regarding providers folder
* Make nrf compile with the example providers for the lighting app
* Make sure linux app build also compiles with SetDeviceInfoProvider
* Restyle
* Remove Zephyr from comments
* Update date for license blurb
diff --git a/examples/lighting-app/nrfconnect/main/AppTask.cpp b/examples/lighting-app/nrfconnect/main/AppTask.cpp
index 12ee2a9..9ee6c8e 100644
--- a/examples/lighting-app/nrfconnect/main/AppTask.cpp
+++ b/examples/lighting-app/nrfconnect/main/AppTask.cpp
@@ -24,6 +24,7 @@
#include "PWMDevice.h"
#include "ThreadUtil.h"
+#include <DeviceInfoProviderImpl.h>
#include <app-common/zap-generated/attribute-id.h>
#include <app-common/zap-generated/attribute-type.h>
#include <app-common/zap-generated/attributes/Accessors.h>
@@ -81,6 +82,8 @@
bool sIsThreadEnabled = false;
bool sHaveBLEConnections = false;
+chip::DeviceLayer::DeviceInfoProviderImpl gExampleDeviceInfoProvider;
+
} // namespace
AppTask AppTask::sAppTask;
@@ -173,6 +176,7 @@
static chip::CommonCaseDeviceServerInitParams initParams;
(void) initParams.InitializeStaticResourcesBeforeServerInit();
+ chip::DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider);
ReturnErrorOnFailure(chip::Server::GetInstance().Init(initParams));
#if CONFIG_CHIP_OTA_REQUESTOR
diff --git a/examples/platform/linux/AppMain.cpp b/examples/platform/linux/AppMain.cpp
index 83f867f..26fea11 100644
--- a/examples/platform/linux/AppMain.cpp
+++ b/examples/platform/linux/AppMain.cpp
@@ -41,6 +41,8 @@
#include <platform/CommissionableDataProvider.h>
#include <platform/DiagnosticDataProvider.h>
+#include <DeviceInfoProviderImpl.h>
+
#if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
#include "CommissionerMain.h"
#include <ControllerShellCommands.h>
@@ -96,6 +98,8 @@
// To hold SPAKE2+ verifier, discriminator, passcode
LinuxCommissionableDataProvider gCommissionableDataProvider;
+chip::DeviceLayer::DeviceInfoProviderImpl gExampleDeviceInfoProvider;
+
#if CHIP_CONFIG_TRANSPORT_TRACE_ENABLED
chip::trace::TraceStream * gTraceStream = nullptr;
#endif // CHIP_CONFIG_TRANSPORT_TRACE_ENABLED
@@ -236,6 +240,7 @@
err = chip::examples::InitCommissionableDataProvider(gCommissionableDataProvider, LinuxDeviceOptions::GetInstance());
SuccessOrExit(err);
DeviceLayer::SetCommissionableDataProvider(&gCommissionableDataProvider);
+ DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider);
err = chip::examples::InitConfigurationManager(reinterpret_cast<ConfigurationManagerImpl &>(ConfigurationMgr()),
LinuxDeviceOptions::GetInstance());
diff --git a/examples/platform/linux/BUILD.gn b/examples/platform/linux/BUILD.gn
index d1be2f2..71586b0 100644
--- a/examples/platform/linux/BUILD.gn
+++ b/examples/platform/linux/BUILD.gn
@@ -51,6 +51,7 @@
}
public_deps = [
+ "${chip_root}/examples/providers:device_info_provider",
"${chip_root}/src/app/server",
"${chip_root}/src/credentials:default_attestation_verifier",
"${chip_root}/src/lib",
diff --git a/examples/providers/BUILD.gn b/examples/providers/BUILD.gn
new file mode 100644
index 0000000..244f70d
--- /dev/null
+++ b/examples/providers/BUILD.gn
@@ -0,0 +1,37 @@
+# Copyright (c) 2022 Project CHIP Authors
+#
+# 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.
+
+import("//build_overrides/build.gni")
+import("//build_overrides/chip.gni")
+
+config("include_providers_dir") {
+ include_dirs = [ "." ]
+}
+
+static_library("device_info_provider") {
+ output_name = "libMatterDeviceInfoProviderExample"
+ output_dir = "${root_out_dir}/lib"
+
+ sources = [
+ "DeviceInfoProviderImpl.cpp",
+ "DeviceInfoProviderImpl.h",
+ ]
+
+ public_deps = [
+ "${chip_root}/src/lib/support",
+ "${chip_root}/src/platform",
+ ]
+
+ public_configs = [ ":include_providers_dir" ]
+}
diff --git a/examples/providers/DeviceInfoProviderImpl.cpp b/examples/providers/DeviceInfoProviderImpl.cpp
new file mode 100644
index 0000000..ba06435
--- /dev/null
+++ b/examples/providers/DeviceInfoProviderImpl.cpp
@@ -0,0 +1,366 @@
+/*
+ *
+ * Copyright (c) 2022 Project CHIP Authors
+ *
+ * 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.
+ */
+#include <DeviceInfoProviderImpl.h>
+
+#include <lib/core/CHIPTLV.h>
+#include <lib/support/CHIPMemString.h>
+#include <lib/support/CodeUtils.h>
+#include <lib/support/DefaultStorageKeyAllocator.h>
+#include <platform/internal/CHIPDeviceLayerInternal.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <cstring>
+
+namespace chip {
+namespace DeviceLayer {
+
+namespace {
+constexpr TLV::Tag kLabelNameTag = TLV::ContextTag(0);
+constexpr TLV::Tag kLabelValueTag = TLV::ContextTag(1);
+} // anonymous namespace
+
+DeviceInfoProviderImpl & DeviceInfoProviderImpl::GetDefaultInstance()
+{
+ static DeviceInfoProviderImpl sInstance;
+ return sInstance;
+}
+
+DeviceInfoProvider::FixedLabelIterator * DeviceInfoProviderImpl::IterateFixedLabel(EndpointId endpoint)
+{
+ return chip::Platform::New<FixedLabelIteratorImpl>(endpoint);
+}
+
+DeviceInfoProviderImpl::FixedLabelIteratorImpl::FixedLabelIteratorImpl(EndpointId endpoint) : mEndpoint(endpoint)
+{
+ mIndex = 0;
+}
+
+size_t DeviceInfoProviderImpl::FixedLabelIteratorImpl::Count()
+{
+ // A hardcoded labelList on all endpoints.
+ return 4;
+}
+
+bool DeviceInfoProviderImpl::FixedLabelIteratorImpl::Next(FixedLabelType & output)
+{
+ bool retval = true;
+
+ // A hardcoded list for testing only
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ const char * labelPtr = nullptr;
+ const char * valuePtr = nullptr;
+
+ VerifyOrReturnError(mIndex < 4, false);
+
+ ChipLogProgress(DeviceLayer, "Get the fixed label with index:%u at endpoint:%d", static_cast<unsigned>(mIndex), mEndpoint);
+
+ switch (mIndex)
+ {
+ case 0:
+ labelPtr = "room";
+ valuePtr = "bedroom 2";
+ break;
+ case 1:
+ labelPtr = "orientation";
+ valuePtr = "North";
+ break;
+ case 2:
+ labelPtr = "floor";
+ valuePtr = "2";
+ break;
+ case 3:
+ labelPtr = "direction";
+ valuePtr = "up";
+ break;
+ default:
+ err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND;
+ break;
+ }
+
+ if (err == CHIP_NO_ERROR)
+ {
+ VerifyOrReturnError(std::strlen(labelPtr) <= kMaxLabelNameLength, false);
+ VerifyOrReturnError(std::strlen(valuePtr) <= kMaxLabelValueLength, false);
+
+ Platform::CopyString(mFixedLabelNameBuf, kMaxLabelNameLength + 1, labelPtr);
+ Platform::CopyString(mFixedLabelValueBuf, kMaxLabelValueLength + 1, valuePtr);
+
+ output.label = CharSpan::fromCharString(mFixedLabelNameBuf);
+ output.value = CharSpan::fromCharString(mFixedLabelValueBuf);
+
+ mIndex++;
+
+ retval = true;
+ }
+ else
+ {
+ retval = false;
+ }
+
+ return retval;
+}
+
+CHIP_ERROR DeviceInfoProviderImpl::SetUserLabelLength(EndpointId endpoint, size_t val)
+{
+ DefaultStorageKeyAllocator keyAlloc;
+
+ return mStorage->SyncSetKeyValue(keyAlloc.UserLabelLengthKey(endpoint), &val, static_cast<uint16_t>(sizeof(val)));
+}
+
+CHIP_ERROR DeviceInfoProviderImpl::GetUserLabelLength(EndpointId endpoint, size_t & val)
+{
+ DefaultStorageKeyAllocator keyAlloc;
+ uint16_t len = static_cast<uint16_t>(sizeof(val));
+
+ return mStorage->SyncGetKeyValue(keyAlloc.UserLabelLengthKey(endpoint), &val, len);
+}
+
+CHIP_ERROR DeviceInfoProviderImpl::SetUserLabelAt(EndpointId endpoint, size_t index, const UserLabelType & userLabel)
+{
+ DefaultStorageKeyAllocator keyAlloc;
+ uint8_t buf[UserLabelTLVMaxSize()];
+ TLV::TLVWriter writer;
+ writer.Init(buf);
+
+ TLV::TLVType outerType;
+ ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType));
+ ReturnErrorOnFailure(writer.PutString(kLabelNameTag, userLabel.label));
+ ReturnErrorOnFailure(writer.PutString(kLabelValueTag, userLabel.value));
+ ReturnErrorOnFailure(writer.EndContainer(outerType));
+
+ return mStorage->SyncSetKeyValue(keyAlloc.UserLabelIndexKey(endpoint, index), buf,
+ static_cast<uint16_t>(writer.GetLengthWritten()));
+}
+
+DeviceInfoProvider::UserLabelIterator * DeviceInfoProviderImpl::IterateUserLabel(EndpointId endpoint)
+{
+ return chip::Platform::New<UserLabelIteratorImpl>(*this, endpoint);
+}
+
+DeviceInfoProviderImpl::UserLabelIteratorImpl::UserLabelIteratorImpl(DeviceInfoProviderImpl & provider, EndpointId endpoint) :
+ mProvider(provider), mEndpoint(endpoint)
+{
+ size_t total = 0;
+
+ ReturnOnFailure(mProvider.GetUserLabelLength(mEndpoint, total));
+ mTotal = total;
+ mIndex = 0;
+}
+
+bool DeviceInfoProviderImpl::UserLabelIteratorImpl::Next(UserLabelType & output)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ VerifyOrReturnError(mIndex < mTotal, false);
+
+ DefaultStorageKeyAllocator keyAlloc;
+ uint8_t buf[UserLabelTLVMaxSize()];
+ uint16_t len = static_cast<uint16_t>(sizeof(buf));
+
+ err = mProvider.mStorage->SyncGetKeyValue(keyAlloc.UserLabelIndexKey(mEndpoint, mIndex), buf, len);
+ VerifyOrReturnError(err == CHIP_NO_ERROR, false);
+
+ TLV::ContiguousBufferTLVReader reader;
+ reader.Init(buf);
+ err = reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag());
+ VerifyOrReturnError(err == CHIP_NO_ERROR, false);
+
+ TLV::TLVType containerType;
+ VerifyOrReturnError(reader.EnterContainer(containerType) == CHIP_NO_ERROR, false);
+
+ chip::CharSpan label;
+ chip::CharSpan value;
+
+ VerifyOrReturnError(reader.Next(kLabelNameTag) == CHIP_NO_ERROR, false);
+ VerifyOrReturnError(reader.Get(label) == CHIP_NO_ERROR, false);
+
+ VerifyOrReturnError(reader.Next(kLabelValueTag) == CHIP_NO_ERROR, false);
+ VerifyOrReturnError(reader.Get(value) == CHIP_NO_ERROR, false);
+
+ VerifyOrReturnError(reader.VerifyEndOfContainer() == CHIP_NO_ERROR, false);
+ VerifyOrReturnError(reader.ExitContainer(containerType) == CHIP_NO_ERROR, false);
+
+ Platform::CopyString(mUserLabelNameBuf, label);
+ Platform::CopyString(mUserLabelValueBuf, value);
+
+ output.label = CharSpan::fromCharString(mUserLabelNameBuf);
+ output.value = CharSpan::fromCharString(mUserLabelValueBuf);
+
+ mIndex++;
+
+ return true;
+}
+
+DeviceInfoProvider::SupportedLocalesIterator * DeviceInfoProviderImpl::IterateSupportedLocales()
+{
+ return chip::Platform::New<SupportedLocalesIteratorImpl>();
+}
+
+size_t DeviceInfoProviderImpl::SupportedLocalesIteratorImpl::Count()
+{
+ // Hardcoded list of locales
+ // {("en-US"), ("de-DE"), ("fr-FR"), ("en-GB"), ("es-ES"), ("zh-CN"), ("it-IT"), ("ja-JP")}
+
+ return 8;
+}
+
+bool DeviceInfoProviderImpl::SupportedLocalesIteratorImpl::Next(CharSpan & output)
+{
+ bool retval = true;
+
+ // Hardcoded list of locales
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ const char * activeLocalePtr = nullptr;
+
+ VerifyOrReturnError(mIndex < 8, false);
+
+ switch (mIndex)
+ {
+ case 0:
+ activeLocalePtr = "en-US";
+ break;
+ case 1:
+ activeLocalePtr = "de-DE";
+ break;
+ case 2:
+ activeLocalePtr = "fr-FR";
+ break;
+ case 3:
+ activeLocalePtr = "en-GB";
+ break;
+ case 4:
+ activeLocalePtr = "es-ES";
+ break;
+ case 5:
+ activeLocalePtr = "zh-CN";
+ break;
+ case 6:
+ activeLocalePtr = "it-IT";
+ break;
+ case 7:
+ activeLocalePtr = "ja-JP";
+ break;
+ default:
+ err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND;
+ break;
+ }
+
+ if (err == CHIP_NO_ERROR)
+ {
+ VerifyOrReturnError(std::strlen(activeLocalePtr) <= kMaxActiveLocaleLength, false);
+
+ Platform::CopyString(mActiveLocaleBuf, kMaxActiveLocaleLength + 1, activeLocalePtr);
+
+ output = CharSpan::fromCharString(mActiveLocaleBuf);
+
+ mIndex++;
+
+ retval = true;
+ }
+ else
+ {
+ retval = false;
+ }
+
+ return retval;
+}
+
+DeviceInfoProvider::SupportedCalendarTypesIterator * DeviceInfoProviderImpl::IterateSupportedCalendarTypes()
+{
+ return chip::Platform::New<SupportedCalendarTypesIteratorImpl>();
+}
+
+size_t DeviceInfoProviderImpl::SupportedCalendarTypesIteratorImpl::Count()
+{
+ // Hardcoded list of strings
+ // {("kBuddhist"), ("kChinese"), ("kCoptic"), ("kEthiopian"), ("kGregorian"), ("kHebrew"), ("kIndian"), ("kJapanese"),
+ // ("kKorean"), ("kPersian"), ("kTaiwanese"), ("kIslamic")}
+
+ return 12;
+}
+
+bool DeviceInfoProviderImpl::SupportedCalendarTypesIteratorImpl::Next(CalendarType & output)
+{
+ bool retval = true;
+
+ // Hardcoded list of Strings that are valid values for the Calendar Types.
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ VerifyOrReturnError(mIndex < 12, false);
+
+ switch (mIndex)
+ {
+ case 0:
+ output = app::Clusters::TimeFormatLocalization::CalendarType::kBuddhist;
+ break;
+ case 1:
+ output = app::Clusters::TimeFormatLocalization::CalendarType::kChinese;
+ break;
+ case 2:
+ output = app::Clusters::TimeFormatLocalization::CalendarType::kCoptic;
+ break;
+ case 3:
+ output = app::Clusters::TimeFormatLocalization::CalendarType::kEthiopian;
+ break;
+ case 4:
+ output = app::Clusters::TimeFormatLocalization::CalendarType::kGregorian;
+ break;
+ case 5:
+ output = app::Clusters::TimeFormatLocalization::CalendarType::kHebrew;
+ break;
+ case 6:
+ output = app::Clusters::TimeFormatLocalization::CalendarType::kIndian;
+ break;
+ case 7:
+ output = app::Clusters::TimeFormatLocalization::CalendarType::kJapanese;
+ break;
+ case 8:
+ output = app::Clusters::TimeFormatLocalization::CalendarType::kKorean;
+ break;
+ case 9:
+ output = app::Clusters::TimeFormatLocalization::CalendarType::kPersian;
+ break;
+ case 10:
+ output = app::Clusters::TimeFormatLocalization::CalendarType::kTaiwanese;
+ break;
+ case 11:
+ output = app::Clusters::TimeFormatLocalization::CalendarType::kIslamic;
+ break;
+ default:
+ err = CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND;
+ break;
+ }
+
+ if (err == CHIP_NO_ERROR)
+ {
+ mIndex++;
+ retval = true;
+ }
+ else
+ {
+ retval = false;
+ }
+
+ return retval;
+}
+
+} // namespace DeviceLayer
+} // namespace chip
diff --git a/examples/providers/DeviceInfoProviderImpl.h b/examples/providers/DeviceInfoProviderImpl.h
new file mode 100644
index 0000000..fc58436
--- /dev/null
+++ b/examples/providers/DeviceInfoProviderImpl.h
@@ -0,0 +1,106 @@
+/*
+ *
+ * Copyright (c) 2022 Project CHIP Authors
+ *
+ * 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.
+ */
+#pragma once
+
+#include <lib/support/EnforceFormat.h>
+#include <platform/DeviceInfoProvider.h>
+
+namespace chip {
+namespace DeviceLayer {
+
+class DeviceInfoProviderImpl : public DeviceInfoProvider
+{
+public:
+ DeviceInfoProviderImpl() = default;
+ ~DeviceInfoProviderImpl() override {}
+
+ // Iterators
+ FixedLabelIterator * IterateFixedLabel(EndpointId endpoint) override;
+ UserLabelIterator * IterateUserLabel(EndpointId endpoint) override;
+ SupportedLocalesIterator * IterateSupportedLocales() override;
+ SupportedCalendarTypesIterator * IterateSupportedCalendarTypes() override;
+
+ static DeviceInfoProviderImpl & GetDefaultInstance();
+
+protected:
+ class FixedLabelIteratorImpl : public FixedLabelIterator
+ {
+ public:
+ FixedLabelIteratorImpl(EndpointId endpoint);
+ size_t Count() override;
+ bool Next(FixedLabelType & output) override;
+ void Release() override { chip::Platform::Delete(this); }
+
+ private:
+ EndpointId mEndpoint = 0;
+ size_t mIndex = 0;
+ char mFixedLabelNameBuf[kMaxLabelNameLength + 1];
+ char mFixedLabelValueBuf[kMaxLabelValueLength + 1];
+ };
+
+ class UserLabelIteratorImpl : public UserLabelIterator
+ {
+ public:
+ UserLabelIteratorImpl(DeviceInfoProviderImpl & provider, EndpointId endpoint);
+ size_t Count() override { return mTotal; }
+ bool Next(UserLabelType & output) override;
+ void Release() override { chip::Platform::Delete(this); }
+
+ private:
+ DeviceInfoProviderImpl & mProvider;
+ EndpointId mEndpoint = 0;
+ size_t mIndex = 0;
+ size_t mTotal = 0;
+ char mUserLabelNameBuf[kMaxLabelNameLength + 1];
+ char mUserLabelValueBuf[kMaxLabelValueLength + 1];
+ };
+
+ class SupportedLocalesIteratorImpl : public SupportedLocalesIterator
+ {
+ public:
+ SupportedLocalesIteratorImpl() = default;
+ size_t Count() override;
+ bool Next(CharSpan & output) override;
+ void Release() override { chip::Platform::Delete(this); }
+
+ private:
+ size_t mIndex = 0;
+ char mActiveLocaleBuf[kMaxActiveLocaleLength + 1];
+ };
+
+ class SupportedCalendarTypesIteratorImpl : public SupportedCalendarTypesIterator
+ {
+ public:
+ SupportedCalendarTypesIteratorImpl() = default;
+ size_t Count() override;
+ bool Next(CalendarType & output) override;
+ void Release() override { chip::Platform::Delete(this); }
+
+ private:
+ size_t mIndex = 0;
+ };
+
+ CHIP_ERROR SetUserLabelLength(EndpointId endpoint, size_t val) override;
+ CHIP_ERROR GetUserLabelLength(EndpointId endpoint, size_t & val) override;
+ CHIP_ERROR SetUserLabelAt(EndpointId endpoint, size_t index, const UserLabelType & userLabel) override;
+
+private:
+ static constexpr size_t UserLabelTLVMaxSize() { return TLV::EstimateStructOverhead(kMaxLabelNameLength, kMaxLabelValueLength); }
+};
+
+} // namespace DeviceLayer
+} // namespace chip
diff --git a/examples/providers/README.md b/examples/providers/README.md
new file mode 100644
index 0000000..dc0ccce
--- /dev/null
+++ b/examples/providers/README.md
@@ -0,0 +1,9 @@
+# Contents
+
+This folder contains example implementations of 'providers' that are generally
+application-specific. Final applications should generally have their own
+implementations of these
+
+## Device Info Provider
+
+Supports device-specific information like labels, locales and calendar types.