• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "dm_log.h"
17 #include "dm_constants.h"
18 #include "dm_auth_state.h"
19 #include "dm_auth_context.h"
20 #include "dm_auth_manager_base.h"
21 #include "dm_auth_state_machine.h"
22 
23 namespace OHOS {
24 namespace DistributedHardware {
25 
DmAuthStateMachine(std::shared_ptr<DmAuthContext> context)26 DmAuthStateMachine::DmAuthStateMachine(std::shared_ptr<DmAuthContext> context)
27 {
28     exceptionEvent_= {
29         DmEventType::ON_ERROR,  // Authentication error, there is a possibility of retry.
30         DmEventType::ON_TIMEOUT,
31         DmEventType::ON_FAIL,  // Authentication failed
32         DmEventType::ON_SCREEN_LOCKED,
33         DmEventType::ON_ULTRASONIC_PIN_TIMEOUT,
34     };
35 
36     running_ = true;
37     direction_ = context->direction;
38 
39     if (direction_ == DM_AUTH_SOURCE) {
40         this->InsertSrcTransTable();
41     } else {
42         this->InsertSinkTransTable();
43     }
44 
45     this->SetCurState(DmAuthStateType::AUTH_IDLE_STATE);
46     thread_ = std::thread(&DmAuthStateMachine::Run, this, context);
47 }
48 
~DmAuthStateMachine()49 DmAuthStateMachine::~DmAuthStateMachine()
50 {
51     Stop();
52 }
53 
InsertSrcTransTable()54 void DmAuthStateMachine::InsertSrcTransTable()
55 {
56     // Source-end state transition table
57     stateTransitionTable_.insert({
58         {DmAuthStateType::AUTH_IDLE_STATE, {DmAuthStateType::AUTH_SRC_START_STATE}},
59         {DmAuthStateType::AUTH_SRC_START_STATE, {DmAuthStateType::AUTH_SRC_NEGOTIATE_STATE}},
60         {DmAuthStateType::AUTH_SRC_NEGOTIATE_STATE, {DmAuthStateType::AUTH_SRC_CONFIRM_STATE}},
61         {DmAuthStateType::AUTH_SRC_CONFIRM_STATE, {
62             DmAuthStateType::AUTH_SRC_PIN_NEGOTIATE_START_STATE,
63             DmAuthStateType::AUTH_SRC_DATA_SYNC_STATE,
64         }},
65         {DmAuthStateType::AUTH_SRC_PIN_NEGOTIATE_START_STATE, {
66             DmAuthStateType::AUTH_SRC_CREDENTIAL_AUTH_START_STATE,
67             DmAuthStateType::AUTH_SRC_PIN_INPUT_STATE,
68             DmAuthStateType::AUTH_SRC_REVERSE_ULTRASONIC_START_STATE,
69             DmAuthStateType::AUTH_SRC_FORWARD_ULTRASONIC_START_STATE,
70             DmAuthStateType::AUTH_SRC_PIN_AUTH_START_STATE,
71             DmAuthStateType::AUTH_SRC_DATA_SYNC_STATE,
72         }},
73         {DmAuthStateType::AUTH_SRC_PIN_INPUT_STATE, {
74             DmAuthStateType::AUTH_SRC_PIN_AUTH_START_STATE,
75         }},
76         {DmAuthStateType::AUTH_SRC_DATA_SYNC_STATE, {DmAuthStateType::AUTH_SRC_FINISH_STATE}},
77         {DmAuthStateType::AUTH_SRC_FINISH_STATE, {}}
78     });
79     InsertCredentialAuthSrcTransTable();
80     InsertUltrasonicSrcTransTable();
81     return;
82 }
83 
InsertCredentialAuthSrcTransTable()84 void DmAuthStateMachine::InsertCredentialAuthSrcTransTable()
85 {
86     stateTransitionTable_.insert({
87         {DmAuthStateType::AUTH_SRC_PIN_AUTH_START_STATE, {
88             DmAuthStateType::AUTH_SRC_PIN_AUTH_MSG_NEGOTIATE_STATE,
89             DmAuthStateType::AUTH_SRC_PIN_NEGOTIATE_START_STATE,
90         }},
91         {DmAuthStateType::AUTH_SRC_PIN_AUTH_MSG_NEGOTIATE_STATE, {
92             DmAuthStateType::AUTH_SRC_PIN_AUTH_DONE_STATE,
93             DmAuthStateType::AUTH_SRC_PIN_NEGOTIATE_START_STATE,
94         }},
95         {DmAuthStateType::AUTH_SRC_PIN_AUTH_DONE_STATE, {
96             DmAuthStateType::AUTH_SRC_CREDENTIAL_EXCHANGE_STATE,
97             DmAuthStateType::AUTH_SRC_PIN_NEGOTIATE_START_STATE,
98         }},
99         {DmAuthStateType::AUTH_SRC_CREDENTIAL_EXCHANGE_STATE, {
100             DmAuthStateType::AUTH_SRC_CREDENTIAL_AUTH_START_STATE,
101             DmAuthStateType::AUTH_SRC_DATA_SYNC_STATE,
102             DmAuthStateType::AUTH_SRC_CREDENTIAL_AUTH_DONE_STATE,
103         }},
104         {DmAuthStateType::AUTH_SRC_CREDENTIAL_AUTH_START_STATE, {
105             DmAuthStateType::AUTH_SRC_CREDENTIAL_AUTH_NEGOTIATE_STATE,
106             DmAuthStateType::AUTH_SRC_SK_DERIVE_STATE,
107         }},
108         {DmAuthStateType::AUTH_SRC_SK_DERIVE_STATE, {
109             DmAuthStateType::AUTH_SRC_DATA_SYNC_STATE,
110             DmAuthStateType::AUTH_SRC_FINISH_STATE,
111         }},
112         {DmAuthStateType::AUTH_SRC_CREDENTIAL_AUTH_NEGOTIATE_STATE, {
113             DmAuthStateType::AUTH_SRC_CREDENTIAL_AUTH_DONE_STATE
114         }},
115         {DmAuthStateType::AUTH_SRC_CREDENTIAL_AUTH_DONE_STATE, {
116             DmAuthStateType::AUTH_SRC_DATA_SYNC_STATE,
117             DmAuthStateType::AUTH_SRC_CREDENTIAL_AUTH_NEGOTIATE_STATE,
118         }},
119     });
120 }
121 
InsertUltrasonicSrcTransTable()122 void DmAuthStateMachine::InsertUltrasonicSrcTransTable()
123 {
124     stateTransitionTable_.insert({
125         {DmAuthStateType::AUTH_SRC_REVERSE_ULTRASONIC_START_STATE, {
126             DmAuthStateType::AUTH_SRC_REVERSE_ULTRASONIC_DONE_STATE,
127             DmAuthStateType::AUTH_SRC_PIN_NEGOTIATE_START_STATE,
128         }},
129         {DmAuthStateType::AUTH_SRC_REVERSE_ULTRASONIC_DONE_STATE, {
130             DmAuthStateType::AUTH_SRC_PIN_AUTH_MSG_NEGOTIATE_STATE,
131             DmAuthStateType::AUTH_SRC_PIN_NEGOTIATE_START_STATE,
132         }},
133         {DmAuthStateType::AUTH_SRC_FORWARD_ULTRASONIC_START_STATE, {
134             DmAuthStateType::AUTH_SRC_FORWARD_ULTRASONIC_DONE_STATE,
135         }},
136         {DmAuthStateType::AUTH_SRC_FORWARD_ULTRASONIC_DONE_STATE, {
137             DmAuthStateType::AUTH_SRC_PIN_AUTH_MSG_NEGOTIATE_STATE,
138             DmAuthStateType::AUTH_SRC_PIN_NEGOTIATE_START_STATE,
139         }},
140         {DmAuthStateType::AUTH_SRC_PIN_NEGOTIATE_START_STATE, {
141             DmAuthStateType::AUTH_SRC_CREDENTIAL_AUTH_START_STATE,
142             DmAuthStateType::AUTH_SRC_PIN_INPUT_STATE,
143             DmAuthStateType::AUTH_SRC_REVERSE_ULTRASONIC_START_STATE,
144             DmAuthStateType::AUTH_SRC_FORWARD_ULTRASONIC_START_STATE,
145             DmAuthStateType::AUTH_SRC_PIN_AUTH_START_STATE,
146         }}
147     });
148     return;
149 }
150 
InsertSinkTransTable()151 void DmAuthStateMachine::InsertSinkTransTable()
152 {
153     // Sink-end state transition table
154     stateTransitionTable_.insert({
155         {DmAuthStateType::AUTH_IDLE_STATE, {DmAuthStateType::AUTH_SINK_NEGOTIATE_STATE}},
156         {DmAuthStateType::AUTH_SINK_NEGOTIATE_STATE, {
157             DmAuthStateType::AUTH_SINK_CONFIRM_STATE,
158         }},
159         {DmAuthStateType::AUTH_SINK_CONFIRM_STATE, {
160             DmAuthStateType::AUTH_SINK_PIN_NEGOTIATE_START_STATE,
161             DmAuthStateType::AUTH_SINK_CREDENTIAL_AUTH_START_STATE,
162         }},
163         {DmAuthStateType::AUTH_SINK_PIN_NEGOTIATE_START_STATE, {
164             DmAuthStateType::AUTH_SINK_PIN_DISPLAY_STATE,
165             DmAuthStateType::AUTH_SINK_REVERSE_ULTRASONIC_START_STATE,
166             DmAuthStateType::AUTH_SINK_FORWARD_ULTRASONIC_START_STATE,
167             DmAuthStateType::AUTH_SINK_PIN_AUTH_START_STATE,
168         }},
169         {DmAuthStateType::AUTH_SINK_PIN_DISPLAY_STATE, {
170             DmAuthStateType::AUTH_SINK_PIN_AUTH_START_STATE,
171         }},
172         {DmAuthStateType::AUTH_SINK_DATA_SYNC_STATE, {DmAuthStateType::AUTH_SINK_FINISH_STATE}},
173         {DmAuthStateType::AUTH_SINK_FINISH_STATE, {}}
174     });
175     InsertCredentialAuthSinkTransTable();
176     InsertUltrasonicSinkTransTable();
177     return;
178 }
179 
InsertCredentialAuthSinkTransTable()180 void DmAuthStateMachine::InsertCredentialAuthSinkTransTable()
181 {
182     stateTransitionTable_.insert({
183         {DmAuthStateType::AUTH_SINK_PIN_AUTH_START_STATE, {
184             DmAuthStateType::AUTH_SINK_PIN_AUTH_MSG_NEGOTIATE_STATE,
185             DmAuthStateType::AUTH_SINK_PIN_NEGOTIATE_START_STATE,
186         }},
187         {DmAuthStateType::AUTH_SINK_PIN_AUTH_MSG_NEGOTIATE_STATE, {
188             DmAuthStateType::AUTH_SINK_PIN_AUTH_DONE_STATE,
189             DmAuthStateType::AUTH_SINK_PIN_NEGOTIATE_START_STATE,
190             DmAuthStateType::AUTH_SINK_FINISH_STATE,
191         }},
192         {DmAuthStateType::AUTH_SINK_PIN_AUTH_DONE_STATE, {
193             DmAuthStateType::AUTH_SINK_CREDENTIAL_EXCHANGE_STATE,
194             DmAuthStateType::AUTH_SINK_CREDENTIAL_AUTH_START_STATE,
195             DmAuthStateType::AUTH_SINK_DATA_SYNC_STATE,
196             DmAuthStateType::AUTH_SINK_FINISH_STATE,
197         }},
198         {DmAuthStateType::AUTH_SINK_CREDENTIAL_EXCHANGE_STATE, {
199             DmAuthStateType::AUTH_SINK_CREDENTIAL_AUTH_START_STATE,
200             DmAuthStateType::AUTH_SINK_SK_DERIVE_STATE,
201         }},
202         {DmAuthStateType::AUTH_SINK_SK_DERIVE_STATE, {
203             DmAuthStateType::AUTH_SINK_DATA_SYNC_STATE,
204             DmAuthStateType::AUTH_SINK_FINISH_STATE,
205         }},
206         {DmAuthStateType::AUTH_SINK_CREDENTIAL_AUTH_START_STATE, {
207             DmAuthStateType::AUTH_SINK_CREDENTIAL_AUTH_NEGOTIATE_STATE
208         }},
209         {DmAuthStateType::AUTH_SINK_CREDENTIAL_AUTH_NEGOTIATE_STATE, {
210             DmAuthStateType::AUTH_SINK_DATA_SYNC_STATE,
211             DmAuthStateType::AUTH_SINK_CREDENTIAL_AUTH_START_STATE
212         }},
213     });
214 }
215 
InsertUltrasonicSinkTransTable()216 void DmAuthStateMachine::InsertUltrasonicSinkTransTable()
217 {
218     stateTransitionTable_.insert({
219         {DmAuthStateType::AUTH_SINK_REVERSE_ULTRASONIC_START_STATE, {
220             DmAuthStateType::AUTH_SINK_REVERSE_ULTRASONIC_DONE_STATE,
221             DmAuthStateType::AUTH_SINK_PIN_NEGOTIATE_START_STATE,
222         }},
223         {DmAuthStateType::AUTH_SINK_REVERSE_ULTRASONIC_DONE_STATE, {
224             DmAuthStateType::AUTH_SINK_PIN_AUTH_MSG_NEGOTIATE_STATE,
225             DmAuthStateType::AUTH_SINK_PIN_NEGOTIATE_START_STATE,
226         }},
227         {DmAuthStateType::AUTH_SINK_FORWARD_ULTRASONIC_START_STATE, {
228             DmAuthStateType::AUTH_SINK_FORWARD_ULTRASONIC_DONE_STATE,
229             DmAuthStateType::AUTH_SINK_PIN_NEGOTIATE_START_STATE,
230         }},
231         {DmAuthStateType::AUTH_SINK_FORWARD_ULTRASONIC_DONE_STATE, {
232             DmAuthStateType::AUTH_SINK_PIN_AUTH_MSG_NEGOTIATE_STATE,
233             DmAuthStateType::AUTH_SINK_PIN_NEGOTIATE_START_STATE,
234         }}
235     });
236     return;
237 }
238 
239 // Notification status transition. The execution status corresponds to specific actions and exception handling.
TransitionTo(std::shared_ptr<DmAuthState> state)240 int32_t DmAuthStateMachine::TransitionTo(std::shared_ptr<DmAuthState> state)
241 {
242     int32_t ret = DM_OK;
243     DmAuthStateType nextState = state->GetStateType();
244     {
245         std::lock_guard lock(stateMutex_);
246         // The states after the finish status are illegal states.
247         if (preState_ == DmAuthStateType::AUTH_SRC_FINISH_STATE ||
248             preState_ == DmAuthStateType::AUTH_SINK_FINISH_STATE) {
249             LOGE("DmAuthStateMachine::TransitionTo next state is invalid.");
250             return ERR_DM_NEXT_STATE_INVALID;
251         }
252         if (this->CheckStateTransitValid(nextState)) {
253             LOGI("DmAuthStateMachine: The state transition from %{public}d to %{public}d.", preState_, nextState);
254             statesQueue_.push(state);
255             preState_ = nextState;
256         } else {
257             // The state transition is invalid.
258             LOGE("DmAuthStateMachine: The state transition does not meet the rule from %{public}d to %{public}d.",
259                 preState_, nextState);
260             ret = ERR_DM_NEXT_STATE_INVALID;
261             reason = ERR_DM_NEXT_STATE_INVALID;
262             if (direction_ == DM_AUTH_SOURCE) {
263                 statesQueue_.push(std::make_shared<AuthSrcFinishState>());
264                 preState_ = DmAuthStateType::AUTH_SRC_FINISH_STATE;
265             } else {
266                 statesQueue_.push(std::make_shared<AuthSinkFinishState>());
267                 preState_ = DmAuthStateType::AUTH_SINK_FINISH_STATE;
268             }
269         }
270     }
271     stateCv_.notify_one();
272     return ret;
273 }
274 
275 /*
276 Expected event in an action, which is used for blocking.
277 When the expected event is complete or other exceptions occur, the actual event is returned.
278 Other normal events continue to be blocked (only in the action).
279 */
WaitExpectEvent(DmEventType eventType)280 DmEventType DmAuthStateMachine::WaitExpectEvent(DmEventType eventType)
281 {
282     /*
283     1. Actual event = Expected event, return actual event
284     2. Actual event = Abnormal event (event timeout). The actual event is also returned.
285     3. Actual event = Other events, continue to block, but there is a timeout limit.
286     */
287     std::unique_lock lock(eventMutex_);
288     auto startTime = std::chrono::high_resolution_clock::now();
289     while (running_.load()) {
290         eventCv_.wait(lock, [&] {
291             return !running_.load() || !eventQueue_.empty();
292         });
293         if (!running_.load()) {
294             return DmEventType::ON_FAIL;
295         }
296 
297         DmEventType actualEventType = eventQueue_.front();
298         eventQueue_.pop();
299         // Determine whether the event is an expected event or abnormal event in list.
300         if (actualEventType == eventType || (exceptionEvent_.find(actualEventType) != exceptionEvent_.end())) {
301             return actualEventType;
302         }
303         // Event Wait Timeout
304         auto elapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>(
305             std::chrono::high_resolution_clock::now() - startTime);
306         if (elapsedTime.count() >= EVENT_TIMEOUT) {
307             break;
308         }
309     }
310     return DmEventType::ON_TIMEOUT;
311 }
312 
313 /*
314 The event is invoked after the event is complete.
315 The event enumeration can be invoked only when the event is triggered.
316 If the event is an abnormal event, the reason or reply of the context must be recorded.
317 */
NotifyEventFinish(DmEventType eventType)318 void DmAuthStateMachine::NotifyEventFinish(DmEventType eventType)
319 {
320     LOGI("DmAuthStateMachine: NotifyEventFinish Event:%{public}d.", eventType);
321     {
322         std::unique_lock lock(eventMutex_);
323         eventQueue_.push(eventType);
324     }
325     eventCv_.notify_one();
326     if (eventType == DmEventType::ON_FAIL) {
327         if (direction_ == DM_AUTH_SOURCE) {
328             this->TransitionTo(std::make_shared<AuthSrcFinishState>());
329         } else {
330             this->TransitionTo(std::make_shared<AuthSinkFinishState>());
331         }
332     }
333 }
334 
335 // Cyclically wait for state transition and execute action.
Run(std::shared_ptr<DmAuthContext> context)336 void DmAuthStateMachine::Run(std::shared_ptr<DmAuthContext> context)
337 {
338     while (running_.load()) {
339         context->state = static_cast<int32_t>(GetCurState());
340         auto state = FetchAndSetCurState();
341         if (!state.has_value()) {
342             break;
343         }
344         if (reason != DM_OK) {
345             context->reason = reason;
346         }
347         // Obtain the status and execute the status action.
348         DmAuthStateType stateType = state.value()->GetStateType();
349         int32_t ret = state.value()->Action(context);
350         if (ret != DM_OK) {
351             LOGE("DmAuthStateMachine::Run err:%{public}d", ret);
352             if (context->reason == DM_OK) {
353                 // If the context reason is not set, set action ret.
354                 context->reason = ret;
355             }
356             context->successFinished = false;
357             if (context->direction == DM_AUTH_SOURCE) {
358                 this->TransitionTo(std::make_shared<AuthSrcFinishState>());
359             } else {
360                 this->TransitionTo(std::make_shared<AuthSinkFinishState>());
361             }
362         } else {
363             LOGI("DmAuthStateMachine::Run ok state:%{public}d", stateType);
364         }
365     }
366     LOGI("DmAuthStateMachine::Run end");
367 }
368 
FetchAndSetCurState()369 std::optional<std::shared_ptr<DmAuthState>> DmAuthStateMachine::FetchAndSetCurState()
370 {
371     std::unique_lock lock(stateMutex_);
372     stateCv_.wait(lock, [&] {
373         return !running_.load() || !statesQueue_.empty();
374     });
375 
376     if (!running_.load()) return std::nullopt;
377 
378     std::shared_ptr<DmAuthState> state = statesQueue_.front();
379     statesQueue_.pop();
380     SetCurState(state->GetStateType());
381     return state;
382 }
383 
NotifyStateWait()384 void DmAuthStateMachine::NotifyStateWait()
385 {
386     std::lock_guard lock(stateMutex_);  // Use locking to prevent signal loss
387     running_.store(false);
388     stateCv_.notify_all();
389 }
390 
NotifyEventWait()391 void DmAuthStateMachine::NotifyEventWait()
392 {
393     std::lock_guard lock(eventMutex_);  // Use locking to prevent signal loss
394     running_.store(false);
395     eventCv_.notify_all();
396 }
397 
Stop()398 void DmAuthStateMachine::Stop()
399 {
400     NotifyStateWait();
401     NotifyEventWait();
402     if (thread_.joinable()) {  // Prevent dobule join
403         thread_.join();
404     }
405 }
406 
SetCurState(DmAuthStateType state)407 void DmAuthStateMachine::SetCurState(DmAuthStateType state)
408 {
409     LOGI("DmAuthStateMachine::SetCurState state: %{public}d", state);
410     curState_ = state;
411 }
412 
GetCurState()413 DmAuthStateType DmAuthStateMachine::GetCurState()
414 {
415     return curState_;
416 }
417 
418 // Verify the validity of the next state transition.
CheckStateTransitValid(DmAuthStateType nextState)419 bool DmAuthStateMachine::CheckStateTransitValid(DmAuthStateType nextState)
420 {
421     // Check whether the next state is AuthSrcFinishState or AuthSinkFinishState
422     // which can directly switch to the state and return.
423     if (direction_ == DM_AUTH_SOURCE) {
424         if (nextState == DmAuthStateType::AUTH_SRC_FINISH_STATE) {
425             return true;
426         }
427     } else {
428         if (nextState == DmAuthStateType::AUTH_SINK_FINISH_STATE) {
429             return true;
430         }
431     }
432 
433     // Check whether the state transition table is met.
434     auto it = stateTransitionTable_.find(preState_);
435     if (it != stateTransitionTable_.end()) {
436         const std::set<DmAuthStateType>& allowedStates = it->second;
437         return allowedStates.find(nextState) != allowedStates.end();
438     }
439     return false;
440 }
441 } // namespace DistributedHardware
442 } // namespace OHOS
443