Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2022 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : /**
19 : * @file
20 : * Provides the implementation of the FailSafeContext object.
21 : */
22 : #include "FailSafeContext.h"
23 : #include <app/icd/server/ICDServerConfig.h>
24 : #if CHIP_CONFIG_ENABLE_ICD_SERVER
25 : #include <app/icd/server/ICDNotifier.h> // nogncheck
26 : #endif
27 : #include <lib/support/SafeInt.h>
28 : #include <platform/CHIPDeviceConfig.h>
29 : #include <platform/ConnectivityManager.h>
30 : #include <platform/internal/CHIPDeviceLayerInternal.h>
31 :
32 : using namespace chip::DeviceLayer;
33 :
34 : namespace chip {
35 : namespace app {
36 :
37 0 : void FailSafeContext::HandleArmFailSafeTimer(System::Layer * layer, void * aAppState)
38 : {
39 0 : FailSafeContext * failSafeContext = reinterpret_cast<FailSafeContext *>(aAppState);
40 0 : failSafeContext->FailSafeTimerExpired();
41 0 : }
42 :
43 0 : void FailSafeContext::HandleMaxCumulativeFailSafeTimer(System::Layer * layer, void * aAppState)
44 : {
45 0 : FailSafeContext * failSafeContext = reinterpret_cast<FailSafeContext *>(aAppState);
46 0 : failSafeContext->FailSafeTimerExpired();
47 0 : }
48 :
49 0 : void FailSafeContext::HandleDisarmFailSafe(intptr_t arg)
50 : {
51 0 : FailSafeContext * failSafeContext = reinterpret_cast<FailSafeContext *>(arg);
52 0 : failSafeContext->DisarmFailSafe();
53 0 : }
54 :
55 4 : void FailSafeContext::SetFailSafeArmed(bool armed)
56 : {
57 : #if CHIP_CONFIG_ENABLE_ICD_SERVER
58 : if (IsFailSafeArmed() != armed)
59 : {
60 : ICDNotifier::GetInstance().BroadcastActiveRequest(ICDListener::KeepActiveFlag::kFailSafeArmed, armed);
61 : }
62 : #endif
63 4 : mFailSafeArmed = armed;
64 4 : }
65 :
66 0 : void FailSafeContext::FailSafeTimerExpired()
67 : {
68 0 : if (!IsFailSafeArmed())
69 : {
70 : // In case this was a pending timer event in event loop, and we had
71 : // done CommissioningComplete or manual disarm.
72 0 : return;
73 : }
74 :
75 0 : ChipLogProgress(FailSafe, "Fail-safe timer expired");
76 0 : ScheduleFailSafeCleanup(mFabricIndex, mAddNocCommandHasBeenInvoked, mUpdateNocCommandHasBeenInvoked);
77 : }
78 :
79 0 : void FailSafeContext::ScheduleFailSafeCleanup(FabricIndex fabricIndex, bool addNocCommandInvoked, bool updateNocCommandInvoked)
80 : {
81 : // Not armed, but busy so cannot rearm (via General Commissioning cluster) until the flushing
82 : // via `HandleDisarmFailSafe` path is complete.
83 : // TODO: This is hacky and we need to remove all this event pushing business, to keep all fail-safe logic-only.
84 0 : mFailSafeBusy = true;
85 :
86 0 : SetFailSafeArmed(false);
87 :
88 0 : ChipDeviceEvent event{ .Type = DeviceEventType::kFailSafeTimerExpired,
89 : .FailSafeTimerExpired = { .fabricIndex = fabricIndex,
90 : .addNocCommandHasBeenInvoked = addNocCommandInvoked,
91 0 : .updateNocCommandHasBeenInvoked = updateNocCommandInvoked } };
92 0 : CHIP_ERROR status = PlatformMgr().PostEvent(&event);
93 :
94 0 : if (status != CHIP_NO_ERROR)
95 : {
96 0 : ChipLogError(FailSafe, "Failed to post fail-safe timer expired: %" CHIP_ERROR_FORMAT, status.Format());
97 : }
98 :
99 0 : PlatformMgr().ScheduleWork(HandleDisarmFailSafe, reinterpret_cast<intptr_t>(this));
100 0 : }
101 :
102 2 : CHIP_ERROR FailSafeContext::ArmFailSafe(FabricIndex accessingFabricIndex, System::Clock::Seconds16 expiryLengthSeconds)
103 : {
104 2 : VerifyOrReturnError(!IsFailSafeBusy(), CHIP_ERROR_INCORRECT_STATE);
105 :
106 2 : CHIP_ERROR err = CHIP_NO_ERROR;
107 2 : bool cancelTimersIfError = false;
108 2 : if (!IsFailSafeArmed())
109 : {
110 2 : System::Clock::Timeout maxCumulativeTimeout = System::Clock::Seconds32(CHIP_DEVICE_CONFIG_MAX_CUMULATIVE_FAILSAFE_SEC);
111 2 : SuccessOrExit(err = DeviceLayer::SystemLayer().StartTimer(maxCumulativeTimeout, HandleMaxCumulativeFailSafeTimer, this));
112 2 : cancelTimersIfError = true;
113 : }
114 :
115 2 : SuccessOrExit(
116 : err = DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds16(expiryLengthSeconds), HandleArmFailSafeTimer, this));
117 :
118 2 : SetFailSafeArmed(true);
119 2 : mFabricIndex = accessingFabricIndex;
120 :
121 2 : exit:
122 :
123 2 : if (err != CHIP_NO_ERROR && cancelTimersIfError)
124 : {
125 0 : DeviceLayer::SystemLayer().CancelTimer(HandleArmFailSafeTimer, this);
126 0 : DeviceLayer::SystemLayer().CancelTimer(HandleMaxCumulativeFailSafeTimer, this);
127 : }
128 2 : return err;
129 : }
130 :
131 2 : void FailSafeContext::DisarmFailSafe()
132 : {
133 2 : DeviceLayer::SystemLayer().CancelTimer(HandleArmFailSafeTimer, this);
134 2 : DeviceLayer::SystemLayer().CancelTimer(HandleMaxCumulativeFailSafeTimer, this);
135 :
136 2 : ResetState();
137 :
138 2 : ChipLogProgress(FailSafe, "Fail-safe cleanly disarmed");
139 2 : }
140 :
141 0 : void FailSafeContext::ForceFailSafeTimerExpiry()
142 : {
143 0 : if (!IsFailSafeArmed())
144 : {
145 0 : return;
146 : }
147 :
148 : // Cancel the timer since we force its action
149 0 : DeviceLayer::SystemLayer().CancelTimer(HandleArmFailSafeTimer, this);
150 0 : DeviceLayer::SystemLayer().CancelTimer(HandleMaxCumulativeFailSafeTimer, this);
151 :
152 0 : FailSafeTimerExpired();
153 : }
154 :
155 : } // namespace app
156 : } // namespace chip
|