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