1 /*
2 * Copyright (c) 2018, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /**
30 * @file
31 * This file includes definitions for non-volatile storage of settings.
32 */
33
34 #include "settings.hpp"
35
36 #include "common/array.hpp"
37 #include "common/code_utils.hpp"
38 #include "common/instance.hpp"
39 #include "common/locator_getters.hpp"
40 #include "common/num_utils.hpp"
41 #include "meshcop/dataset.hpp"
42 #include "thread/mle.hpp"
43
44 namespace ot {
45
46 RegisterLogModule("Settings");
47
48 //---------------------------------------------------------------------------------------------------------------------
49 // SettingsBase
50
51 // LCOV_EXCL_START
52
53 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
54
Log(Action aAction) const55 void SettingsBase::NetworkInfo::Log(Action aAction) const
56 {
57 LogInfo("%s NetworkInfo {rloc:0x%04x, extaddr:%s, role:%s, mode:0x%02x, version:%u, keyseq:0x%lx, ...",
58 ActionToString(aAction), GetRloc16(), GetExtAddress().ToString().AsCString(),
59 Mle::RoleToString(static_cast<Mle::DeviceRole>(GetRole())), GetDeviceMode(), GetVersion(),
60 ToUlong(GetKeySequence()));
61
62 LogInfo("... pid:0x%lx, mlecntr:0x%lx, maccntr:0x%lx, mliid:%s}", ToUlong(GetPreviousPartitionId()),
63 ToUlong(GetMleFrameCounter()), ToUlong(GetMacFrameCounter()), GetMeshLocalIid().ToString().AsCString());
64 }
65
Log(Action aAction) const66 void SettingsBase::ParentInfo::Log(Action aAction) const
67 {
68 LogInfo("%s ParentInfo {extaddr:%s, version:%u}", ActionToString(aAction), GetExtAddress().ToString().AsCString(),
69 GetVersion());
70 }
71
72 #if OPENTHREAD_FTD
Log(Action aAction) const73 void SettingsBase::ChildInfo::Log(Action aAction) const
74 {
75 LogInfo("%s ChildInfo {rloc:0x%04x, extaddr:%s, timeout:%lu, mode:0x%02x, version:%u}", ActionToString(aAction),
76 GetRloc16(), GetExtAddress().ToString().AsCString(), ToUlong(GetTimeout()), GetMode(), GetVersion());
77 }
78 #endif
79
80 #if OPENTHREAD_CONFIG_DUA_ENABLE
Log(Action aAction) const81 void SettingsBase::DadInfo::Log(Action aAction) const
82 {
83 LogInfo("%s DadInfo {DadCounter:%2d}", ActionToString(aAction), GetDadCounter());
84 }
85 #endif
86
87 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
LogPrefix(Action aAction,Key aKey,const Ip6::Prefix & aPrefix)88 void SettingsBase::LogPrefix(Action aAction, Key aKey, const Ip6::Prefix &aPrefix)
89 {
90 LogInfo("%s %s %s", ActionToString(aAction), KeyToString(aKey), aPrefix.ToString().AsCString());
91 }
92 #endif
93
94 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
Log(Action aAction) const95 void SettingsBase::SrpClientInfo::Log(Action aAction) const
96 {
97 LogInfo("%s SrpClientInfo {Server:[%s]:%u}", ActionToString(aAction), GetServerAddress().ToString().AsCString(),
98 GetServerPort());
99 }
100 #endif
101
102 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE && OPENTHREAD_CONFIG_SRP_SERVER_PORT_SWITCH_ENABLE
Log(Action aAction) const103 void SettingsBase::SrpServerInfo::Log(Action aAction) const
104 {
105 LogInfo("%s SrpServerInfo {port:%u}", ActionToString(aAction), GetPort());
106 }
107 #endif
108
109 #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
Log(Action aAction) const110 void SettingsBase::BorderAgentId::Log(Action aAction) const
111 {
112 char buffer[sizeof(BorderAgentId) * 2 + 1];
113 StringWriter sw(buffer, sizeof(buffer));
114
115 sw.AppendHexBytes(GetId().mId, sizeof(BorderAgentId));
116 LogInfo("%s BorderAgentId {id:%s}", ActionToString(aAction), buffer);
117 }
118 #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
119
120 #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
121
122 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
ActionToString(Action aAction)123 const char *SettingsBase::ActionToString(Action aAction)
124 {
125 static const char *const kActionStrings[] = {
126 "Read", // (0) kActionRead
127 "Saved", // (1) kActionSave
128 "Re-saved", // (2) kActionResave
129 "Deleted", // (3) kActionDelete
130 #if OPENTHREAD_FTD
131 "Added", // (4) kActionAdd,
132 "Removed", // (5) kActionRemove,
133 "Deleted all" // (6) kActionDeleteAll
134 #endif
135 };
136
137 static_assert(0 == kActionRead, "kActionRead value is incorrect");
138 static_assert(1 == kActionSave, "kActionSave value is incorrect");
139 static_assert(2 == kActionResave, "kActionResave value is incorrect");
140 static_assert(3 == kActionDelete, "kActionDelete value is incorrect");
141 #if OPENTHREAD_FTD
142 static_assert(4 == kActionAdd, "kActionAdd value is incorrect");
143 static_assert(5 == kActionRemove, "kActionRemove value is incorrect");
144 static_assert(6 == kActionDeleteAll, "kActionDeleteAll value is incorrect");
145 #endif
146
147 return kActionStrings[aAction];
148 }
149 #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
150
151 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
KeyToString(Key aKey)152 const char *SettingsBase::KeyToString(Key aKey)
153 {
154 static const char *const kKeyStrings[] = {
155 "", // (0) (Unused)
156 "ActiveDataset", // (1) kKeyActiveDataset
157 "PendingDataset", // (2) kKeyPendingDataset
158 "NetworkInfo", // (3) kKeyNetworkInfo
159 "ParentInfo", // (4) kKeyParentInfo
160 "ChildInfo", // (5) kKeyChildInfo
161 "", // (6) Removed (previously auto-start).
162 "SlaacIidSecretKey", // (7) kKeySlaacIidSecretKey
163 "DadInfo", // (8) kKeyDadInfo
164 "", // (9) Removed (previously OMR prefix).
165 "", // (10) Removed (previously on-link prefix).
166 "SrpEcdsaKey", // (11) kKeySrpEcdsaKey
167 "SrpClientInfo", // (12) kKeySrpClientInfo
168 "SrpServerInfo", // (13) kKeySrpServerInfo
169 "", // (14) Removed (previously NAT64 prefix)
170 "BrUlaPrefix", // (15) kKeyBrUlaPrefix
171 "BrOnLinkPrefixes", // (16) kKeyBrOnLinkPrefixes
172 "BorderAgentId" // (17) kKeyBorderAgentId
173 };
174
175 static_assert(1 == kKeyActiveDataset, "kKeyActiveDataset value is incorrect");
176 static_assert(2 == kKeyPendingDataset, "kKeyPendingDataset value is incorrect");
177 static_assert(3 == kKeyNetworkInfo, "kKeyNetworkInfo value is incorrect");
178 static_assert(4 == kKeyParentInfo, "kKeyParentInfo value is incorrect");
179 static_assert(5 == kKeyChildInfo, "kKeyChildInfo value is incorrect");
180 static_assert(7 == kKeySlaacIidSecretKey, "kKeySlaacIidSecretKey value is incorrect");
181 static_assert(8 == kKeyDadInfo, "kKeyDadInfo value is incorrect");
182 static_assert(11 == kKeySrpEcdsaKey, "kKeySrpEcdsaKey value is incorrect");
183 static_assert(12 == kKeySrpClientInfo, "kKeySrpClientInfo value is incorrect");
184 static_assert(13 == kKeySrpServerInfo, "kKeySrpServerInfo value is incorrect");
185 static_assert(15 == kKeyBrUlaPrefix, "kKeyBrUlaPrefix value is incorrect");
186 static_assert(16 == kKeyBrOnLinkPrefixes, "kKeyBrOnLinkPrefixes is incorrect");
187 static_assert(17 == kKeyBorderAgentId, "kKeyBorderAgentId is incorrect");
188
189 static_assert(kLastKey == kKeyBorderAgentId, "kLastKey is not valid");
190
191 OT_ASSERT(aKey <= kLastKey);
192
193 return kKeyStrings[aKey];
194 }
195 #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
196
197 // LCOV_EXCL_STOP
198
199 //---------------------------------------------------------------------------------------------------------------------
200 // Settings
201
202 // This array contains sensitive keys that should be stored in the secure area.
203 const uint16_t Settings::kSensitiveKeys[] = {
204 SettingsBase::kKeyActiveDataset,
205 SettingsBase::kKeyPendingDataset,
206 SettingsBase::kKeySrpEcdsaKey,
207 };
208
Init(void)209 void Settings::Init(void) { Get<SettingsDriver>().Init(kSensitiveKeys, GetArrayLength(kSensitiveKeys)); }
210
Deinit(void)211 void Settings::Deinit(void) { Get<SettingsDriver>().Deinit(); }
212
Wipe(void)213 void Settings::Wipe(void)
214 {
215 Get<SettingsDriver>().Wipe();
216 LogInfo("Wiped all info");
217 }
218
KeyForDatasetType(MeshCoP::Dataset::Type aType)219 Settings::Key Settings::KeyForDatasetType(MeshCoP::Dataset::Type aType)
220 {
221 return (aType == MeshCoP::Dataset::kActive) ? kKeyActiveDataset : kKeyPendingDataset;
222 }
223
SaveOperationalDataset(MeshCoP::Dataset::Type aType,const MeshCoP::Dataset & aDataset)224 Error Settings::SaveOperationalDataset(MeshCoP::Dataset::Type aType, const MeshCoP::Dataset &aDataset)
225 {
226 Key key = KeyForDatasetType(aType);
227 Error error = Get<SettingsDriver>().Set(key, aDataset.GetBytes(), aDataset.GetSize());
228
229 Log(kActionSave, error, key);
230
231 return error;
232 }
233
ReadOperationalDataset(MeshCoP::Dataset::Type aType,MeshCoP::Dataset & aDataset) const234 Error Settings::ReadOperationalDataset(MeshCoP::Dataset::Type aType, MeshCoP::Dataset &aDataset) const
235 {
236 Error error = kErrorNone;
237 uint16_t length = MeshCoP::Dataset::kMaxSize;
238
239 SuccessOrExit(error = Get<SettingsDriver>().Get(KeyForDatasetType(aType), aDataset.GetBytes(), &length));
240 VerifyOrExit(length <= MeshCoP::Dataset::kMaxSize, error = kErrorNotFound);
241
242 aDataset.SetSize(length);
243
244 exit:
245 return error;
246 }
247
DeleteOperationalDataset(MeshCoP::Dataset::Type aType)248 Error Settings::DeleteOperationalDataset(MeshCoP::Dataset::Type aType)
249 {
250 Key key = KeyForDatasetType(aType);
251 Error error = Get<SettingsDriver>().Delete(key);
252
253 Log(kActionDelete, error, key);
254
255 return error;
256 }
257
258 #if OPENTHREAD_FTD
AddChildInfo(const ChildInfo & aChildInfo)259 Error Settings::AddChildInfo(const ChildInfo &aChildInfo)
260 {
261 Error error = Get<SettingsDriver>().Add(kKeyChildInfo, &aChildInfo, sizeof(aChildInfo));
262
263 Log(kActionAdd, error, kKeyChildInfo, &aChildInfo);
264
265 return error;
266 }
267
DeleteAllChildInfo(void)268 Error Settings::DeleteAllChildInfo(void)
269 {
270 Error error = Get<SettingsDriver>().Delete(kKeyChildInfo);
271
272 Log(kActionDeleteAll, error, kKeyChildInfo);
273
274 return error;
275 }
276
ChildInfoIterator(Instance & aInstance)277 Settings::ChildInfoIterator::ChildInfoIterator(Instance &aInstance)
278 : SettingsBase(aInstance)
279 , mIndex(0)
280 , mIsDone(false)
281 {
282 Read();
283 }
284
Advance(void)285 void Settings::ChildInfoIterator::Advance(void)
286 {
287 if (!mIsDone)
288 {
289 mIndex++;
290 Read();
291 }
292 }
293
Delete(void)294 Error Settings::ChildInfoIterator::Delete(void)
295 {
296 Error error = kErrorNone;
297
298 VerifyOrExit(!mIsDone, error = kErrorInvalidState);
299 SuccessOrExit(error = Get<SettingsDriver>().Delete(kKeyChildInfo, mIndex));
300
301 exit:
302 Log(kActionRemove, error, kKeyChildInfo, &mChildInfo);
303 return error;
304 }
305
Read(void)306 void Settings::ChildInfoIterator::Read(void)
307 {
308 uint16_t length = sizeof(ChildInfo);
309 Error error;
310
311 mChildInfo.Init();
312 SuccessOrExit(
313 error = Get<SettingsDriver>().Get(kKeyChildInfo, mIndex, reinterpret_cast<uint8_t *>(&mChildInfo), &length));
314
315 exit:
316 Log(kActionRead, error, kKeyChildInfo, &mChildInfo);
317 mIsDone = (error != kErrorNone);
318 }
319 #endif // OPENTHREAD_FTD
320
321 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
AddOrUpdateBrOnLinkPrefix(const BrOnLinkPrefix & aBrOnLinkPrefix)322 Error Settings::AddOrUpdateBrOnLinkPrefix(const BrOnLinkPrefix &aBrOnLinkPrefix)
323 {
324 Error error = kErrorNone;
325 int index = 0;
326 BrOnLinkPrefix brPrefix;
327 bool didUpdate = false;
328
329 while (ReadBrOnLinkPrefix(index, brPrefix) == kErrorNone)
330 {
331 if (brPrefix.GetPrefix() == aBrOnLinkPrefix.GetPrefix())
332 {
333 if (brPrefix.GetLifetime() == aBrOnLinkPrefix.GetLifetime())
334 {
335 // Existing entry fully matches `aBrOnLinkPrefix`.
336 // No need to make any changes.
337 ExitNow();
338 }
339
340 SuccessOrExit(error = Get<SettingsDriver>().Delete(kKeyBrOnLinkPrefixes, index));
341 didUpdate = true;
342 break;
343 }
344
345 index++;
346 }
347
348 SuccessOrExit(error = Get<SettingsDriver>().Add(kKeyBrOnLinkPrefixes, &aBrOnLinkPrefix, sizeof(BrOnLinkPrefix)));
349 brPrefix.Log(didUpdate ? "Updated" : "Added");
350
351 exit:
352 return error;
353 }
354
RemoveBrOnLinkPrefix(const Ip6::Prefix & aPrefix)355 Error Settings::RemoveBrOnLinkPrefix(const Ip6::Prefix &aPrefix)
356 {
357 Error error = kErrorNotFound;
358 BrOnLinkPrefix brPrefix;
359
360 for (int index = 0; ReadBrOnLinkPrefix(index, brPrefix) == kErrorNone; index++)
361 {
362 if (brPrefix.GetPrefix() == aPrefix)
363 {
364 SuccessOrExit(error = Get<SettingsDriver>().Delete(kKeyBrOnLinkPrefixes, index));
365 brPrefix.Log("Removed");
366 break;
367 }
368 }
369
370 exit:
371 return error;
372 }
373
DeleteAllBrOnLinkPrefixes(void)374 Error Settings::DeleteAllBrOnLinkPrefixes(void) { return Get<SettingsDriver>().Delete(kKeyBrOnLinkPrefixes); }
375
ReadBrOnLinkPrefix(int aIndex,BrOnLinkPrefix & aBrOnLinkPrefix)376 Error Settings::ReadBrOnLinkPrefix(int aIndex, BrOnLinkPrefix &aBrOnLinkPrefix)
377 {
378 uint16_t length = sizeof(BrOnLinkPrefix);
379
380 aBrOnLinkPrefix.Init();
381
382 return Get<SettingsDriver>().Get(kKeyBrOnLinkPrefixes, aIndex, &aBrOnLinkPrefix, &length);
383 }
384
Log(const char * aActionText) const385 void Settings::BrOnLinkPrefix::Log(const char *aActionText) const
386 {
387 OT_UNUSED_VARIABLE(aActionText);
388
389 LogInfo("%s %s entry {prefix:%s,lifetime:%lu}", aActionText, KeyToString(kKeyBrOnLinkPrefixes),
390 GetPrefix().ToString().AsCString(), ToUlong(GetLifetime()));
391 }
392
393 #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
394
ReadEntry(Key aKey,void * aValue,uint16_t aMaxLength) const395 Error Settings::ReadEntry(Key aKey, void *aValue, uint16_t aMaxLength) const
396 {
397 Error error;
398 uint16_t length = aMaxLength;
399
400 error = Get<SettingsDriver>().Get(aKey, aValue, &length);
401 Log(kActionRead, error, aKey, aValue);
402
403 return error;
404 }
405
SaveEntry(Key aKey,const void * aValue,void * aPrev,uint16_t aLength)406 Error Settings::SaveEntry(Key aKey, const void *aValue, void *aPrev, uint16_t aLength)
407 {
408 Error error = kErrorNone;
409 uint16_t readLength = aLength;
410 Action action = kActionSave;
411
412 if ((Get<SettingsDriver>().Get(aKey, aPrev, &readLength) == kErrorNone) && (readLength == aLength) &&
413 (memcmp(aValue, aPrev, aLength) == 0))
414 {
415 action = kActionResave;
416 }
417 else
418 {
419 error = Get<SettingsDriver>().Set(aKey, aValue, aLength);
420 }
421
422 Log(action, error, aKey, aValue);
423
424 return error;
425 }
426
DeleteEntry(Key aKey)427 Error Settings::DeleteEntry(Key aKey)
428 {
429 Error error = Get<SettingsDriver>().Delete(aKey);
430
431 Log(kActionDelete, error, aKey);
432
433 return error;
434 }
435
Log(Action aAction,Error aError,Key aKey,const void * aValue)436 void Settings::Log(Action aAction, Error aError, Key aKey, const void *aValue)
437 {
438 OT_UNUSED_VARIABLE(aAction);
439 OT_UNUSED_VARIABLE(aKey);
440 OT_UNUSED_VARIABLE(aError);
441 OT_UNUSED_VARIABLE(aValue);
442
443 if (aError != kErrorNone)
444 {
445 // Log error if log level is at "warn" or higher.
446
447 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
448 const char *actionText = "";
449
450 switch (aAction)
451 {
452 case kActionSave:
453 case kActionResave:
454 actionText = "saving";
455 break;
456
457 case kActionDelete:
458 VerifyOrExit(aError != kErrorNotFound);
459 actionText = "deleting";
460 break;
461
462 #if OPENTHREAD_FTD
463 case kActionAdd:
464 actionText = "adding";
465 break;
466
467 case kActionRemove:
468 VerifyOrExit(aError != kErrorNotFound);
469 actionText = "removing";
470 break;
471
472 case kActionDeleteAll:
473 VerifyOrExit(aError != kErrorNotFound);
474 actionText = "deleting all";
475 break;
476 #endif
477 case kActionRead:
478 ExitNow();
479 }
480
481 LogWarn("Error %s %s %s", ErrorToString(aError), actionText, KeyToString(aKey));
482
483 #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN)
484
485 ExitNow();
486 }
487
488 // We reach here when `aError` is `kErrorNone`.
489 // Log success if log level is at "info" or higher.
490
491 #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
492 if (aValue != nullptr)
493 {
494 switch (aKey)
495 {
496 case kKeyNetworkInfo:
497 reinterpret_cast<const NetworkInfo *>(aValue)->Log(aAction);
498 break;
499
500 case kKeyParentInfo:
501 reinterpret_cast<const ParentInfo *>(aValue)->Log(aAction);
502 break;
503
504 #if OPENTHREAD_FTD
505 case kKeyChildInfo:
506 reinterpret_cast<const ChildInfo *>(aValue)->Log(aAction);
507 break;
508 #endif
509
510 #if OPENTHREAD_CONFIG_DUA_ENABLE
511 case kKeyDadInfo:
512 reinterpret_cast<const DadInfo *>(aValue)->Log(aAction);
513 break;
514 #endif
515
516 #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
517 case kKeyBrUlaPrefix:
518 LogPrefix(aAction, aKey, *reinterpret_cast<const Ip6::Prefix *>(aValue));
519 break;
520 #endif
521
522 #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE
523 case kKeySrpClientInfo:
524 reinterpret_cast<const SrpClientInfo *>(aValue)->Log(aAction);
525 break;
526 #endif
527
528 #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE && OPENTHREAD_CONFIG_SRP_SERVER_PORT_SWITCH_ENABLE
529 case kKeySrpServerInfo:
530 reinterpret_cast<const SrpServerInfo *>(aValue)->Log(aAction);
531 break;
532 #endif
533
534 #if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
535 case kKeyBorderAgentId:
536 reinterpret_cast<const BorderAgentId *>(aValue)->Log(aAction);
537 break;
538 #endif
539
540 default:
541 // For any other keys, we do not want to include the value
542 // in the log, so even if it is given we set `aValue` to
543 // `nullptr`. This ensures that we just log the action and
544 // the key.
545 aValue = nullptr;
546 break;
547 }
548 }
549
550 if (aValue == nullptr)
551 {
552 LogInfo("%s %s", ActionToString(aAction), KeyToString(aKey));
553 }
554 #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO)
555
556 exit:
557 return;
558 }
559
560 } // namespace ot
561
562 //---------------------------------------------------------------------------------------------------------------------
563 // Default/weak implementation of settings platform APIs
564
otPlatSettingsSetCriticalKeys(otInstance * aInstance,const uint16_t * aKeys,uint16_t aKeysLength)565 OT_TOOL_WEAK void otPlatSettingsSetCriticalKeys(otInstance *aInstance, const uint16_t *aKeys, uint16_t aKeysLength)
566 {
567 OT_UNUSED_VARIABLE(aInstance);
568 OT_UNUSED_VARIABLE(aKeys);
569 OT_UNUSED_VARIABLE(aKeysLength);
570 }
571