1 /*
2 * Copyright (c) 2021-2021 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 #define HST_LOG_TAG "StateMachine"
17
18 #include "state_machine.h"
19 #include "osal/utils/util.h"
20 #include "utils/steady_clock.h"
21
22 namespace OHOS {
23 namespace Media {
24 namespace Record {
StateMachine(RecorderExecutor & executor)25 StateMachine::StateMachine(RecorderExecutor& executor)
26 : Task("StateMachine"),
27 intentSync_("fsmSync"),
28 curState_(std::make_shared<InitState>(StateId::INIT, executor)),
29 jobs_("StateMachineJobQue")
30 {
31 AddState(curState_);
32 AddState(std::make_shared<ErrorState>(StateId::ERROR, executor));
33 AddState(std::make_shared<RecordingSettingState>(StateId::RECORDING_SETTING, executor));
34 AddState(std::make_shared<ReadyState>(StateId::READY, executor));
35 AddState(std::make_shared<RecordingState>(StateId::RECORDING, executor));
36 AddState(std::make_shared<PauseState>(StateId::PAUSE, executor));
37 }
38
Stop()39 void StateMachine::Stop()
40 {
41 MEDIA_LOG_D("Stop called.");
42 while (!jobs_.Empty()) {
43 OSAL::SleepFor(10); // 10
44 }
45 jobs_.SetActive(false);
46 Task::Stop();
47 }
48
SetStateCallback(StateChangeCallback * callback)49 void StateMachine::SetStateCallback(StateChangeCallback* callback)
50 {
51 callback_ = callback;
52 }
53
GetCurrentState() const54 const std::string& StateMachine::GetCurrentState() const
55 {
56 return curState_->GetName();
57 }
58
GetCurrentStateId() const59 StateId StateMachine::GetCurrentStateId() const
60 {
61 return curState_->GetStateId();
62 }
63
SendEvent(Intent intent,const Plugin::Any & param) const64 ErrorCode StateMachine::SendEvent(Intent intent, const Plugin::Any& param) const
65 {
66 return const_cast<StateMachine*>(this)->SendEvent(intent, param);
67 }
68
SendEvent(Intent intent,const Plugin::Any & param)69 ErrorCode StateMachine::SendEvent(Intent intent, const Plugin::Any& param)
70 {
71 constexpr int timeoutMs = 30000; /// 30000 ms
72 ErrorCode errorCode = ErrorCode::ERROR_TIMED_OUT;
73 if (!intentSync_.WaitFor(intent,
74 [this, intent, param] { return SendEventAsync(intent, param) == ErrorCode::SUCCESS; },
75 timeoutMs,
76 errorCode)) {
77 MEDIA_LOG_E("SendEvent timeout, intent: " PUBLIC_LOG_D32, static_cast<int>(intent));
78 }
79 return errorCode;
80 }
81
SendEventAsync(Intent intent,const Plugin::Any & param) const82 ErrorCode StateMachine::SendEventAsync(Intent intent, const Plugin::Any& param) const
83 {
84 return const_cast<StateMachine*>(this)->SendEventAsync(intent, param);
85 }
86
SendEventAsync(Intent intent,const Plugin::Any & param)87 ErrorCode StateMachine::SendEventAsync(Intent intent, const Plugin::Any& param)
88 {
89 MEDIA_LOG_D("SendEventAsync, intent: " PUBLIC_LOG_D32, static_cast<int>(intent));
90 if (jobs_.Push([this, intent, param]() -> Action { return ProcessIntent(intent, param); })) {
91 return ErrorCode::SUCCESS;
92 }
93 return ErrorCode::ERROR_UNKNOWN;
94 }
95
ProcessIntent(Intent intent,const Plugin::Any & param)96 Action StateMachine::ProcessIntent(Intent intent, const Plugin::Any& param)
97 {
98 MEDIA_LOG_D("ProcessIntent, curState: " PUBLIC_LOG_S ", intent: " PUBLIC_LOG_D32 ".",
99 curState_->GetName().c_str(), intent);
100 PROFILE_BEGIN("ProcessIntent, curState: " PUBLIC_LOG_S ", intent: " PUBLIC_LOG_D32 ".",
101 curState_->GetName().c_str(), intent);
102 OSAL::ScopedLock lock(mutex_);
103 lastIntent = intent;
104 ErrorCode rtv = ErrorCode::SUCCESS;
105 Action nextAction = Action::ACTION_BUTT;
106 std::tie(rtv, nextAction) = curState_->Execute(intent, param);
107 if (nextAction != Action::ACTION_BUTT) {
108 if (rtv == ErrorCode::SUCCESS) {
109 rtv = ProcAction(nextAction);
110 } else {
111 (void)ProcAction(nextAction);
112 }
113 }
114 OnIntentExecuted(intent, nextAction, rtv);
115 PROFILE_END("ProcessIntent, curState: " PUBLIC_LOG_S ", intent: " PUBLIC_LOG_D32 ".",
116 curState_->GetName().c_str(), intent);
117 return (rtv == ErrorCode::SUCCESS) ? nextAction : Action::ACTION_BUTT;
118 }
119
DoTask()120 void StateMachine::DoTask()
121 {
122 #ifdef UNIT_TEST
123 constexpr int timeoutMs = 500;
124 auto job = jobs_.Pop(timeoutMs);
125 #else
126 auto job = jobs_.Pop();
127 #endif
128 if (!job) {
129 return;
130 }
131 auto action = job();
132 switch (action) {
133 case Action::ACTION_PENDING:
134 pendingJobs_.push(job);
135 break;
136 case Action::TRANS_TO_INIT:
137 case Action::TRANS_TO_RECORDING_SETTING:
138 case Action::TRANS_TO_READY:
139 case Action::TRANS_TO_RECORDING:
140 case Action::TRANS_TO_PAUSE: {
141 if (!pendingJobs_.empty()) {
142 job = pendingJobs_.front();
143 pendingJobs_.pop();
144 action = job();
145 if (action == Action::ACTION_PENDING) {
146 pendingJobs_.push(job);
147 }
148 }
149 break;
150 }
151 case Action::ACTION_BUTT:
152 // fall through
153 default:
154 break;
155 }
156 }
157
AddState(const std::shared_ptr<State> & state)158 void StateMachine::AddState(const std::shared_ptr<State>& state)
159 {
160 states_[state->GetStateId()] = state;
161 }
162
ProcAction(Action nextAction)163 ErrorCode StateMachine::ProcAction(Action nextAction)
164 {
165 std::shared_ptr<State> nextState = nullptr;
166 switch (nextAction) {
167 case Action::TRANS_TO_INIT:
168 nextState = states_[StateId::INIT];
169 break;
170 case Action::TRANS_TO_RECORDING_SETTING:
171 nextState = states_[StateId::RECORDING_SETTING];
172 break;
173 case Action::TRANS_TO_READY:
174 nextState = states_[StateId::READY];
175 break;
176 case Action::TRANS_TO_RECORDING:
177 nextState = states_[StateId::RECORDING];
178 break;
179 case Action::TRANS_TO_PAUSE:
180 nextState = states_[StateId::PAUSE];
181 break;
182 case Action::TRANS_TO_ERROR:
183 nextState = states_[StateId::ERROR];
184 break;
185 default:
186 break;
187 }
188 ErrorCode ret = ErrorCode::SUCCESS;
189 if (nextState) {
190 ret = TransitionTo(nextState);
191 }
192 return ret;
193 }
194
TransitionTo(const std::shared_ptr<State> & state)195 ErrorCode StateMachine::TransitionTo(const std::shared_ptr<State>& state)
196 {
197 if (state == nullptr) {
198 MEDIA_LOG_E("TransitionTo, nullptr for state");
199 return ErrorCode::ERROR_INVALID_PARAMETER_VALUE;
200 }
201 ErrorCode rtv = ErrorCode::SUCCESS;
202 if (state != curState_) {
203 curState_->Exit();
204 curState_ = state;
205 Action nextAction;
206 std::tie(rtv, nextAction) = curState_->Enter(lastIntent);
207 if (rtv == ErrorCode::SUCCESS) {
208 rtv = ProcAction(nextAction);
209 }
210 if (callback_) {
211 callback_->OnStateChanged(curState_->GetStateId());
212 }
213 }
214 return rtv;
215 }
216
OnIntentExecuted(Intent intent,Action action,ErrorCode result)217 void StateMachine::OnIntentExecuted(Intent intent, Action action, ErrorCode result)
218 {
219 MEDIA_LOG_D("OnIntentExecuted, curState: " PUBLIC_LOG_S ", intent: " PUBLIC_LOG_D32 ", action: " PUBLIC_LOG
220 "d, result: " PUBLIC_LOG_S, curState_->GetName().c_str(),
221 static_cast<int>(intent), static_cast<int>(action), GetErrorName(result));
222 if (action == Action::ACTION_PENDING) {
223 return;
224 }
225 if (intent == Intent::NOTIFY_READY && action == Action::TRANS_TO_RECORDING) {
226 intentSync_.Notify(Intent::START, result);
227 } else {
228 intentSync_.Notify(intent, result);
229 }
230 }
231 } // namespace Record
232 } // namespace Media
233 } // namespace OHOS