Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2023 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 : #pragma once
18 :
19 : #include <app/icd/server/ICDServerConfig.h>
20 :
21 : #include <app/AppConfig.h>
22 : #include <app/SubscriptionsInfoProvider.h>
23 : #include <app/TestEventTriggerDelegate.h>
24 : #include <app/icd/server/ICDConfigurationData.h>
25 : #include <app/icd/server/ICDNotifier.h>
26 : #include <app/icd/server/ICDStateObserver.h>
27 : #include <credentials/FabricTable.h>
28 : #include <crypto/SessionKeystore.h>
29 : #include <lib/support/BitFlags.h>
30 : #include <messaging/ExchangeMgr.h>
31 : #include <platform/CHIPDeviceConfig.h>
32 : #include <platform/internal/CHIPDeviceLayerInternal.h>
33 : #include <system/SystemClock.h>
34 :
35 : #if CHIP_CONFIG_ENABLE_ICD_CIP
36 : #include <app/icd/server/ICDCheckInBackOffStrategy.h> // nogncheck
37 : #include <app/icd/server/ICDCheckInSender.h> // nogncheck
38 : #include <app/icd/server/ICDMonitoringTable.h> // nogncheck
39 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
40 :
41 : namespace chip {
42 : namespace Crypto {
43 : using SymmetricKeystore = SessionKeystore;
44 : }
45 : } // namespace chip
46 :
47 : namespace chip {
48 : namespace app {
49 :
50 : // Forward declaration of TestICDManager tests to allow it to be friend with ICDManager
51 : // Used in unit tests
52 : class TestICDManager_TestShouldCheckInMsgsBeSentAtActiveModeFunction_Test;
53 :
54 : /**
55 : * @brief ICD Manager is responsible of processing the events and triggering the correct action for an ICD
56 : */
57 : class ICDManager : public ICDListener, public TestEventTriggerHandler
58 : {
59 : public:
60 : /**
61 : * @brief This structure is used for the creation an ObjectPool of ICDStateObserver pointers
62 : */
63 : struct ObserverPointer
64 : {
65 10 : ObserverPointer(ICDStateObserver * obs) : mObserver(obs) {}
66 10 : ~ObserverPointer() { mObserver = nullptr; }
67 : ICDStateObserver * mObserver;
68 : };
69 :
70 : enum class OperationalState : uint8_t
71 : {
72 : IdleMode,
73 : ActiveMode,
74 : };
75 :
76 : /**
77 : * @brief This enum class represents all ICDStateObserver callbacks available from the
78 : * mStateObserverPool for the ICDManager.
79 : *
80 : * EnterActiveMode, TransitionToIdle and EnterIdleMode will always be called as a trio in the same order.
81 : * Each event will only be called once per cycle.
82 : * EnterActiveMode will always be called first, when the ICD has transitioned to ActiveMode.
83 : * TransitionToIdle will always be second. This event will only be called the first time there is
84 : * `ICD_ACTIVE_TIME_JITTER_MS` remaining to the ActiveMode timer.
85 : * When this event is called, the ICD is still in ActiveMode.
86 : * If the ActiveMode timer is increased due to the TransitionToIdle event, the event will not be called a second time in
87 : * a given cycle.
88 : * OnEnterIdleMode will always the third event and indicates that the ICD has transitioned to IdleMode.
89 : *
90 : * The ICDModeChange event can occur independently from the EnterActiveMode, TransitionToIdle and EnterIdleMode.
91 : * It will typically happen at the ICDManager init when a client is already registered with the ICD before the
92 : * OnEnterIdleMode event or when a client sends a register command after the OnEnterActiveMode event. Nothing prevents
93 : * the ICDModeChange event from happening multiple times per cycle or while the ICD is in IdleMode.
94 : *
95 : * See src/app/icd/server/ICDStateObserver.h for more information on the APIs each event triggers
96 : */
97 : enum class ObserverEventType : uint8_t
98 : {
99 : EnterActiveMode,
100 : EnterIdleMode,
101 : TransitionToIdle,
102 : ICDModeChange,
103 : };
104 :
105 : /**
106 : * @brief Verifier template function
107 : * This type can be used to implement specific verifiers that can be used in the CheckInMessagesWouldBeSent function.
108 : * The goal is to avoid having multiple functions that implement the iterator loop with only the check changing.
109 : *
110 : * @return true: if at least one Check-In message would be sent
111 : * false: No Check-In messages would be sent
112 : */
113 : using ShouldCheckInMsgsBeSentFunction = bool(FabricIndex aFabricIndex, NodeId subjectID);
114 :
115 : ICDManager() = default;
116 10 : ~ICDManager() = default;
117 :
118 : /*
119 : Builder function to set all necessary members for the ICDManager class
120 : */
121 :
122 : #if CHIP_CONFIG_ENABLE_ICD_CIP
123 : ICDManager & SetPersistentStorageDelegate(PersistentStorageDelegate * storage)
124 : {
125 : mStorage = storage;
126 : return *this;
127 : };
128 :
129 : ICDManager & SetFabricTable(FabricTable * fabricTable)
130 : {
131 : mFabricTable = fabricTable;
132 : return *this;
133 : };
134 :
135 : ICDManager & SetSymmetricKeyStore(Crypto::SymmetricKeystore * symmetricKeystore)
136 : {
137 : mSymmetricKeystore = symmetricKeystore;
138 : return *this;
139 : };
140 :
141 : ICDManager & SetExchangeManager(Messaging::ExchangeManager * exchangeManager)
142 : {
143 : mExchangeManager = exchangeManager;
144 : return *this;
145 : };
146 :
147 : ICDManager & SetSubscriptionsInfoProvider(SubscriptionsInfoProvider * subInfoProvider)
148 : {
149 : mSubInfoProvider = subInfoProvider;
150 : return *this;
151 : };
152 :
153 : ICDManager & SetICDCheckInBackOffStrategy(ICDCheckInBackOffStrategy * strategy)
154 : {
155 : mICDCheckInBackOffStrategy = strategy;
156 : return *this;
157 : };
158 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
159 :
160 : /**
161 : * @brief Validates that the ICDManager has all the necessary members to function and initializes the class
162 : */
163 : void Init();
164 : void Shutdown();
165 :
166 : /**
167 : * @brief SupportsFeature verifies if a given FeatureMap bit is enabled
168 : *
169 : * @param[in] feature FeatureMap bit to verify
170 : *
171 : * @return true: if the FeatureMap bit is enabled in the ICDM cluster attribute.
172 : * false: if the FeatureMap bit is not enabled in the ICDM cluster attribute.
173 : * if we failed to read the FeatureMap attribute.
174 : */
175 : bool SupportsFeature(Clusters::IcdManagement::Feature feature);
176 :
177 : ICDConfigurationData::ICDMode GetICDMode() { return ICDConfigurationData::GetInstance().GetICDMode(); };
178 :
179 : OperationalState GetOperaionalState() { return mOperationalState; };
180 :
181 : /**
182 : * @brief Adds the referenced observer in parameters to the mStateObserverPool
183 : * A maximum of CHIP_CONFIG_ICD_OBSERVERS_POOL_SIZE observers can be concurrently registered
184 : *
185 : * @return The pointer to the pool object, or null if it could not be added.
186 : */
187 : ObserverPointer * RegisterObserver(ICDStateObserver * observer);
188 :
189 : /**
190 : * @brief Remove the referenced observer in parameters from the mStateObserverPool
191 : * If the observer is not present in the object pool, we do nothing
192 : */
193 : void ReleaseObserver(ICDStateObserver * observer);
194 :
195 : /**
196 : * @brief Ensures that the remaining Active Mode duration is at least the smaller of 30000 milliseconds and stayActiveDuration.
197 : *
198 : * @param[in] stayActiveDuration The duration (in milliseconds) requested by the client to stay in Active Mode
199 : * @return The duration (in milliseconds) the device will stay in Active Mode
200 : */
201 : uint32_t StayActiveRequest(uint32_t stayActiveDuration);
202 :
203 : /**
204 : * @brief TestEventTriggerHandler for the ICD feature set
205 : *
206 : * @param[in] eventTrigger Event trigger to handle.
207 : *
208 : * @return CHIP_ERROR CHIP_NO_ERROR - No erros during the processing
209 : * CHIP_ERROR_INVALID_ARGUMENT - eventTrigger isn't a valid value
210 : */
211 : CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override;
212 :
213 : #if CHIP_CONFIG_ENABLE_ICD_CIP
214 : /**
215 : * @brief Trigger the ICDManager to send Check-In message if necessary
216 : *
217 : * @param[in] function to use to determine if we need to send check-in messages
218 : */
219 : void TriggerCheckInMessages(const std::function<ShouldCheckInMsgsBeSentFunction> & function);
220 :
221 : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
222 : /**
223 : * @brief Set mSubCheckInBootCheckExecuted to true
224 : * Function allows the InteractionModelEngine to notify the ICDManager that the boot up subscription resumption has been
225 : * completed.
226 : */
227 : void SetBootUpResumeSubscriptionExecuted() { mIsBootUpResumeSubscriptionExecuted = true; };
228 : #endif // !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
229 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
230 :
231 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
232 : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
233 : bool GetIsBootUpResumeSubscriptionExecuted() { return mIsBootUpResumeSubscriptionExecuted; };
234 : #endif // !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
235 : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
236 :
237 : // Implementation of ICDListener functions.
238 : // Callers must origin from the chip task context or hold the ChipStack lock.
239 :
240 : void OnNetworkActivity() override;
241 : void OnKeepActiveRequest(KeepActiveFlags request) override;
242 : void OnActiveRequestWithdrawal(KeepActiveFlags request) override;
243 :
244 : #if CHIP_CONFIG_ENABLE_ICD_DSLS
245 : void OnSITModeRequest() override;
246 : void OnSITModeRequestWithdrawal() override;
247 : #endif
248 :
249 : void OnICDManagementServerEvent(ICDManagementEvents event) override;
250 : void OnSubscriptionReport() override;
251 :
252 : private:
253 : // TODO : Once <gtest/gtest_prod.h> can be included, use FRIEND_TEST for the friend class.
254 : friend class TestICDManager_TestShouldCheckInMsgsBeSentAtActiveModeFunction_Test;
255 :
256 : /**
257 : * @brief UpdateICDMode evaluates in which mode the ICD can be in; SIT or LIT mode.
258 : * If the current operating mode does not match the evaluated operating mode, function updates the ICDMode and triggers
259 : * all necessary operations.
260 : * For a SIT ICD, this function does nothing.
261 : * For a LIT ICD, the function checks if the ICD has a registration in the ICDMonitoringTable to determine which ICDMode
262 : * the ICD must be in.
263 : */
264 : void UpdateICDMode();
265 :
266 : /**
267 : * @brief UpdateOperationState updates the OperationState of the ICD to the requested one.
268 : * IdleMode -> IdleMode : No actions are necessary, do nothing.
269 : * IdleMode -> ActiveMode : Transition the device to ActiveMode, start the ActiveMode timer and trigger all necessary
270 : * operations. These operations could be : Send Check-In messages
271 : * Send subscription reports
272 : * Process user actions
273 : * ActiveMode -> ActiveMode : Increase remaining ActiveMode timer to one ActiveModeThreshold.
274 : * If ActiveModeThreshold is 0, do nothing.
275 : * ActiveMode -> IdleMode : Transition ICD to IdleMode and start the IdleMode timer.
276 : *
277 : * @param state requested OperationalState for the ICD to transition to
278 : */
279 : void UpdateOperationState(OperationalState state);
280 :
281 : /**
282 : * @brief Set or Remove a keep ActiveMode requirement for the given flag
283 : * If state is true and the ICD is in IdleMode, transition the ICD to ActiveMode
284 : * If state is false and the ICD is in ActiveMode, check whether we can transition the ICD to IdleMode.
285 : * If we can, transition the ICD to IdleMode.
286 : *
287 : * @param flag KeepActiveFlag to remove or add
288 : * @param state true: adding a flag requirement
289 : * false: removing a flag requirement
290 : */
291 : void SetKeepActiveModeRequirements(KeepActiveFlags flag, bool state);
292 :
293 : /**
294 : * @brief Associates the ObserverEventType parameters to the correct
295 : * ICDStateObservers function and calls it for all observers in the mStateObserverPool
296 : */
297 : void postObserverEvent(ObserverEventType event);
298 :
299 : /**
300 : * @brief Hepler function that extends the ActiveMode timer as well as the Active Mode Jitter timer for the transition to
301 : * idle mode event.
302 : */
303 : void ExtendActiveMode(System::Clock::Milliseconds16 extendDuration);
304 :
305 : /**
306 : * @brief Timer callback function for when the IdleMode timer expires
307 : *
308 : * @param appState pointer to the ICDManager
309 : */
310 : static void OnIdleModeDone(System::Layer * aLayer, void * appState);
311 :
312 : /**
313 : * @brief Timer callback function for when the ActiveMode timer expires
314 : *
315 : * @param appState pointer to the ICDManager
316 : */
317 : static void OnActiveModeDone(System::Layer * aLayer, void * appState);
318 :
319 : /**
320 : * @brief Timer Callback function called shortly before the device enters idle mode to allow checks to be made.
321 : * This is currently only called once to prevent entering in a loop if some events re-trigger this check (for instance if
322 : * a check for subscriptions before entering idle mode leads to emiting a report, we will re-enter UpdateOperationState
323 : * and check again for subscription, etc.)
324 : *
325 : * @param appState pointer to the ICDManager
326 : */
327 : static void OnTransitionToIdle(System::Layer * aLayer, void * appState);
328 :
329 : #if CHIP_CONFIG_ENABLE_ICD_CIP
330 : /**
331 : * @brief Function triggers all necessary Check-In messages to be sent.
332 : *
333 : * @note For each ICDMonitoring entry, we check if should send a Check-In message with
334 : * ShouldCheckInMsgsBeSentAtActiveModeFunction. If we should, we allocate an ICDCheckInSender which tries to send a
335 : * Check-In message to the registered client.
336 : */
337 : void SendCheckInMsgs();
338 :
339 : /**
340 : * @brief See function implementation in .cpp for details on this function.
341 : */
342 : bool ShouldCheckInMsgsBeSentAtActiveModeFunction(FabricIndex aFabricIndex, NodeId subjectID);
343 :
344 : /**
345 : * @brief Function checks if at least one client registration would require a Check-In message
346 : *
347 : * @param[in] function function to use to determine if a Check-In message would be sent for a given registration
348 : *
349 : * @return true At least one registration would require an Check-In message if we were entering ActiveMode.
350 : * @return false None of the registration would require a Check-In message either because there are no registration or
351 : * because they all have associated subscriptions.
352 : */
353 : bool CheckInMessagesWouldBeSent(const std::function<ShouldCheckInMsgsBeSentFunction> & function);
354 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
355 :
356 : KeepActiveFlags mKeepActiveFlags{ 0 };
357 :
358 : // Initialize mOperationalState to ActiveMode so the init sequence at bootup triggers the IdleMode behaviour first.
359 : OperationalState mOperationalState = OperationalState::ActiveMode;
360 : bool mTransitionToIdleCalled = false;
361 : ObjectPool<ObserverPointer, CHIP_CONFIG_ICD_OBSERVERS_POOL_SIZE> mStateObserverPool;
362 : uint8_t mOpenExchangeContextCount = 0;
363 :
364 : #if CHIP_CONFIG_ENABLE_ICD_DSLS
365 : bool mSITModeRequested = false;
366 : #endif
367 :
368 : #if CHIP_CONFIG_ENABLE_ICD_CIP
369 : uint8_t mCheckInRequestCount = 0;
370 :
371 : #if !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
372 : bool mIsBootUpResumeSubscriptionExecuted = false;
373 : #endif // !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
374 :
375 : PersistentStorageDelegate * mStorage = nullptr;
376 : FabricTable * mFabricTable = nullptr;
377 : Messaging::ExchangeManager * mExchangeManager = nullptr;
378 : Crypto::SymmetricKeystore * mSymmetricKeystore = nullptr;
379 : SubscriptionsInfoProvider * mSubInfoProvider = nullptr;
380 : ICDCheckInBackOffStrategy * mICDCheckInBackOffStrategy = nullptr;
381 : ObjectPool<ICDCheckInSender, (CHIP_CONFIG_ICD_CLIENTS_SUPPORTED_PER_FABRIC * CHIP_CONFIG_MAX_FABRICS)> mICDSenderPool;
382 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
383 : };
384 :
385 : } // namespace app
386 : } // namespace chip
|