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 : // See ICDConfigurationData::SetModeDurations
178 : CHIP_ERROR SetModeDurations(Optional<System::Clock::Milliseconds32> activeModeDuration,
179 : Optional<System::Clock::Milliseconds32> idleModeDuration)
180 : {
181 : return ICDConfigurationData::GetInstance().SetModeDurations(activeModeDuration, idleModeDuration);
182 : };
183 :
184 : ICDConfigurationData::ICDMode GetICDMode() { return ICDConfigurationData::GetInstance().GetICDMode(); };
185 :
186 : OperationalState GetOperaionalState() { return mOperationalState; };
187 :
188 : /**
189 : * @brief Adds the referenced observer in parameters to the mStateObserverPool
190 : * A maximum of CHIP_CONFIG_ICD_OBSERVERS_POOL_SIZE observers can be concurrently registered
191 : *
192 : * @return The pointer to the pool object, or null if it could not be added.
193 : */
194 : ObserverPointer * RegisterObserver(ICDStateObserver * observer);
195 :
196 : /**
197 : * @brief Remove the referenced observer in parameters from the mStateObserverPool
198 : * If the observer is not present in the object pool, we do nothing
199 : */
200 : void ReleaseObserver(ICDStateObserver * observer);
201 :
202 : /**
203 : * @brief Ensures that the remaining Active Mode duration is at least the smaller of 30000 milliseconds and stayActiveDuration.
204 : *
205 : * @param[in] stayActiveDuration The duration (in milliseconds) requested by the client to stay in Active Mode
206 : * @return The duration (in milliseconds) the device will stay in Active Mode
207 : */
208 : uint32_t StayActiveRequest(uint32_t stayActiveDuration);
209 :
210 : /**
211 : * @brief TestEventTriggerHandler for the ICD feature set
212 : *
213 : * @param[in] eventTrigger Event trigger to handle.
214 : *
215 : * @return CHIP_ERROR CHIP_NO_ERROR - No erros during the processing
216 : * CHIP_ERROR_INVALID_ARGUMENT - eventTrigger isn't a valid value
217 : */
218 : CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override;
219 :
220 : #if CHIP_CONFIG_ENABLE_ICD_CIP
221 : /**
222 : * @brief Trigger the ICDManager to send Check-In message if necessary
223 : *
224 : * @param[in] function to use to determine if we need to send check-in messages
225 : */
226 : void TriggerCheckInMessages(const std::function<ShouldCheckInMsgsBeSentFunction> & function);
227 :
228 : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
229 : /**
230 : * @brief Set mSubCheckInBootCheckExecuted to true
231 : * Function allows the InteractionModelEngine to notify the ICDManager that the boot up subscription resumption has been
232 : * completed.
233 : */
234 : void SetBootUpResumeSubscriptionExecuted() { mIsBootUpResumeSubscriptionExecuted = true; };
235 : #endif // !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
236 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
237 :
238 : #if CONFIG_BUILD_FOR_HOST_UNIT_TEST
239 : #if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
240 : bool GetIsBootUpResumeSubscriptionExecuted() { return mIsBootUpResumeSubscriptionExecuted; };
241 : #endif // !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
242 : #endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
243 :
244 : // Implementation of ICDListener functions.
245 : // Callers must origin from the chip task context or hold the ChipStack lock.
246 :
247 : void OnNetworkActivity() override;
248 : void OnKeepActiveRequest(KeepActiveFlags request) override;
249 : void OnActiveRequestWithdrawal(KeepActiveFlags request) override;
250 :
251 : #if CHIP_CONFIG_ENABLE_ICD_DSLS
252 : void OnSITModeRequest() override;
253 : void OnSITModeRequestWithdrawal() override;
254 : #endif
255 :
256 : void OnICDManagementServerEvent(ICDManagementEvents event) override;
257 : void OnSubscriptionReport() override;
258 :
259 : private:
260 : // TODO : Once <gtest/gtest_prod.h> can be included, use FRIEND_TEST for the friend class.
261 : friend class TestICDManager_TestShouldCheckInMsgsBeSentAtActiveModeFunction_Test;
262 :
263 : /**
264 : * @brief UpdateICDMode evaluates in which mode the ICD can be in; SIT or LIT mode.
265 : * If the current operating mode does not match the evaluated operating mode, function updates the ICDMode and triggers
266 : * all necessary operations.
267 : * For a SIT ICD, this function does nothing.
268 : * For a LIT ICD, the function checks if the ICD has a registration in the ICDMonitoringTable to determine which ICDMode
269 : * the ICD must be in.
270 : */
271 : void UpdateICDMode();
272 :
273 : /**
274 : * @brief UpdateOperationState updates the OperationState of the ICD to the requested one.
275 : * IdleMode -> IdleMode : No actions are necessary, do nothing.
276 : * IdleMode -> ActiveMode : Transition the device to ActiveMode, start the ActiveMode timer and trigger all necessary
277 : * operations. These operations could be : Send Check-In messages
278 : * Send subscription reports
279 : * Process user actions
280 : * ActiveMode -> ActiveMode : Increase remaining ActiveMode timer to one ActiveModeThreshold.
281 : * If ActiveModeThreshold is 0, do nothing.
282 : * ActiveMode -> IdleMode : Transition ICD to IdleMode and start the IdleMode timer.
283 : *
284 : * @param state requested OperationalState for the ICD to transition to
285 : */
286 : void UpdateOperationState(OperationalState state);
287 :
288 : /**
289 : * @brief Set or Remove a keep ActiveMode requirement for the given flag
290 : * If state is true and the ICD is in IdleMode, transition the ICD to ActiveMode
291 : * If state is false and the ICD is in ActiveMode, check whether we can transition the ICD to IdleMode.
292 : * If we can, transition the ICD to IdleMode.
293 : *
294 : * @param flag KeepActiveFlag to remove or add
295 : * @param state true: adding a flag requirement
296 : * false: removing a flag requirement
297 : */
298 : void SetKeepActiveModeRequirements(KeepActiveFlags flag, bool state);
299 :
300 : /**
301 : * @brief Associates the ObserverEventType parameters to the correct
302 : * ICDStateObservers function and calls it for all observers in the mStateObserverPool
303 : */
304 : void postObserverEvent(ObserverEventType event);
305 :
306 : /**
307 : * @brief Hepler function that extends the ActiveMode timer as well as the Active Mode Jitter timer for the transition to
308 : * idle mode event.
309 : */
310 : void ExtendActiveMode(System::Clock::Milliseconds16 extendDuration);
311 :
312 : /**
313 : * @brief Timer callback function for when the IdleMode timer expires
314 : *
315 : * @param appState pointer to the ICDManager
316 : */
317 : static void OnIdleModeDone(System::Layer * aLayer, void * appState);
318 :
319 : /**
320 : * @brief Timer callback function for when the ActiveMode timer expires
321 : *
322 : * @param appState pointer to the ICDManager
323 : */
324 : static void OnActiveModeDone(System::Layer * aLayer, void * appState);
325 :
326 : /**
327 : * @brief Timer Callback function called shortly before the device enters idle mode to allow checks to be made.
328 : * This is currently only called once to prevent entering in a loop if some events re-trigger this check (for instance if
329 : * a check for subscriptions before entering idle mode leads to emiting a report, we will re-enter UpdateOperationState
330 : * and check again for subscription, etc.)
331 : *
332 : * @param appState pointer to the ICDManager
333 : */
334 : static void OnTransitionToIdle(System::Layer * aLayer, void * appState);
335 :
336 : #if CHIP_CONFIG_ENABLE_ICD_CIP
337 : /**
338 : * @brief Function triggers all necessary Check-In messages to be sent.
339 : *
340 : * @note For each ICDMonitoring entry, we check if should send a Check-In message with
341 : * ShouldCheckInMsgsBeSentAtActiveModeFunction. If we should, we allocate an ICDCheckInSender which tries to send a
342 : * Check-In message to the registered client.
343 : */
344 : void SendCheckInMsgs();
345 :
346 : /**
347 : * @brief See function implementation in .cpp for details on this function.
348 : */
349 : bool ShouldCheckInMsgsBeSentAtActiveModeFunction(FabricIndex aFabricIndex, NodeId subjectID);
350 :
351 : /**
352 : * @brief Function checks if at least one client registration would require a Check-In message
353 : *
354 : * @param[in] function function to use to determine if a Check-In message would be sent for a given registration
355 : *
356 : * @return true At least one registration would require an Check-In message if we were entering ActiveMode.
357 : * @return false None of the registration would require a Check-In message either because there are no registration or
358 : * because they all have associated subscriptions.
359 : */
360 : bool CheckInMessagesWouldBeSent(const std::function<ShouldCheckInMsgsBeSentFunction> & function);
361 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
362 :
363 : KeepActiveFlags mKeepActiveFlags{ 0 };
364 :
365 : // Initialize mOperationalState to ActiveMode so the init sequence at bootup triggers the IdleMode behaviour first.
366 : OperationalState mOperationalState = OperationalState::ActiveMode;
367 : bool mTransitionToIdleCalled = false;
368 : ObjectPool<ObserverPointer, CHIP_CONFIG_ICD_OBSERVERS_POOL_SIZE> mStateObserverPool;
369 : uint8_t mOpenExchangeContextCount = 0;
370 :
371 : #if CHIP_CONFIG_ENABLE_ICD_DSLS
372 : bool mSITModeRequested = false;
373 : #endif
374 :
375 : #if CHIP_CONFIG_ENABLE_ICD_CIP
376 : uint8_t mCheckInRequestCount = 0;
377 :
378 : #if !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
379 : bool mIsBootUpResumeSubscriptionExecuted = false;
380 : #endif // !CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION && CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
381 :
382 : PersistentStorageDelegate * mStorage = nullptr;
383 : FabricTable * mFabricTable = nullptr;
384 : Messaging::ExchangeManager * mExchangeManager = nullptr;
385 : Crypto::SymmetricKeystore * mSymmetricKeystore = nullptr;
386 : SubscriptionsInfoProvider * mSubInfoProvider = nullptr;
387 : ICDCheckInBackOffStrategy * mICDCheckInBackOffStrategy = nullptr;
388 : ObjectPool<ICDCheckInSender, (CHIP_CONFIG_ICD_CLIENTS_SUPPORTED_PER_FABRIC * CHIP_CONFIG_MAX_FABRICS)> mICDSenderPool;
389 : #endif // CHIP_CONFIG_ENABLE_ICD_CIP
390 : };
391 :
392 : } // namespace app
393 : } // namespace chip
|