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