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 = {
90 : .fabricIndex = fabricIndex,
91 : .addNocCommandHasBeenInvoked = addNocCommandInvoked,
92 : .updateNocCommandHasBeenInvoked = updateNocCommandInvoked,
93 0 : .updateTermsAndConditionsHasBeenInvoked = mUpdateTermsAndConditionsHasBeenInvoked,
94 0 : } };
95 0 : CHIP_ERROR status = PlatformMgr().PostEvent(&event);
96 :
97 0 : if (status != CHIP_NO_ERROR)
98 : {
99 0 : ChipLogError(FailSafe, "Failed to post fail-safe timer expired: %" CHIP_ERROR_FORMAT, status.Format());
100 : }
101 :
102 0 : PlatformMgr().ScheduleWork(HandleDisarmFailSafe, reinterpret_cast<intptr_t>(this));
103 0 : }
104 :
105 2 : CHIP_ERROR FailSafeContext::ArmFailSafe(FabricIndex accessingFabricIndex, System::Clock::Seconds16 expiryLengthSeconds)
106 : {
107 2 : VerifyOrReturnError(!IsFailSafeBusy(), CHIP_ERROR_INCORRECT_STATE);
108 :
109 2 : CHIP_ERROR err = CHIP_NO_ERROR;
110 2 : bool cancelTimersIfError = false;
111 2 : if (!IsFailSafeArmed())
112 : {
113 2 : System::Clock::Timeout maxCumulativeTimeout = System::Clock::Seconds32(CHIP_DEVICE_CONFIG_MAX_CUMULATIVE_FAILSAFE_SEC);
114 2 : SuccessOrExit(err = DeviceLayer::SystemLayer().StartTimer(maxCumulativeTimeout, HandleMaxCumulativeFailSafeTimer, this));
115 2 : cancelTimersIfError = true;
116 : }
117 :
118 2 : SuccessOrExit(
119 : err = DeviceLayer::SystemLayer().StartTimer(System::Clock::Seconds16(expiryLengthSeconds), HandleArmFailSafeTimer, this));
120 :
121 2 : SetFailSafeArmed(true);
122 2 : mFabricIndex = accessingFabricIndex;
123 :
124 2 : exit:
125 :
126 2 : if (err != CHIP_NO_ERROR && cancelTimersIfError)
127 : {
128 0 : DeviceLayer::SystemLayer().CancelTimer(HandleArmFailSafeTimer, this);
129 0 : DeviceLayer::SystemLayer().CancelTimer(HandleMaxCumulativeFailSafeTimer, this);
130 : }
131 2 : return err;
132 : }
133 :
134 2 : void FailSafeContext::DisarmFailSafe()
135 : {
136 2 : DeviceLayer::SystemLayer().CancelTimer(HandleArmFailSafeTimer, this);
137 2 : DeviceLayer::SystemLayer().CancelTimer(HandleMaxCumulativeFailSafeTimer, this);
138 :
139 2 : ResetState();
140 :
141 2 : ChipLogProgress(FailSafe, "Fail-safe cleanly disarmed");
142 2 : }
143 :
144 0 : void FailSafeContext::ForceFailSafeTimerExpiry()
145 : {
146 0 : if (!IsFailSafeArmed())
147 : {
148 0 : return;
149 : }
150 :
151 : // Cancel the timer since we force its action
152 0 : DeviceLayer::SystemLayer().CancelTimer(HandleArmFailSafeTimer, this);
153 0 : DeviceLayer::SystemLayer().CancelTimer(HandleMaxCumulativeFailSafeTimer, this);
154 :
155 0 : FailSafeTimerExpired();
156 : }
157 :
158 : } // namespace app
159 : } // namespace chip
|