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