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 "eos_state.h"
20 #include "foundation/osal/utils/util.h"
21 #include "foundation/utils/steady_clock.h"
22 #include "idle_state.h"
23 #include "init_state.h"
24 #include "pause_state.h"
25 #include "playing_state.h"
26 #include "preparing_state.h"
27 #include "ready_state.h"
28 #include "stopped_state.h"
29
30 namespace OHOS {
31 namespace Media {
StateMachine(PlayExecutor & executor)32 StateMachine::StateMachine(PlayExecutor& executor)
33 : Task("StateMachine"),
34 intentSync_("fsmSync"),
35 curState_(std::make_shared<IdleState>(StateId::IDLE, executor)),
36 jobs_("StateMachineJobQue")
37 {
38 AddState(curState_);
39 AddState(std::make_shared<InitState>(StateId::INIT, executor));
40 AddState(std::make_shared<PreparingState>(StateId::PREPARING, executor));
41 AddState(std::make_shared<ReadyState>(StateId::READY, executor));
42 AddState(std::make_shared<PlayingState>(StateId::PLAYING, executor));
43 AddState(std::make_shared<PauseState>(StateId::PAUSE, executor));
44 AddState(std::make_shared<StoppedState>(StateId::STOPPED, executor));
45 AddState(std::make_shared<EosState>(StateId::EOS, executor));
46 }
47
Stop()48 void StateMachine::Stop()
49 {
50 MEDIA_LOG_I("StateMachine stop called.");
51 while (!jobs_.Empty()) {
52 OSAL::SleepFor(10); // 10
53 }
54 jobs_.SetActive(false);
55 Task::Stop();
56 }
57
SetStateCallback(StateChangeCallback * callback)58 void StateMachine::SetStateCallback(StateChangeCallback* callback)
59 {
60 callback_ = callback;
61 }
62
GetCurrentState() const63 const std::string& StateMachine::GetCurrentState() const
64 {
65 return curState_->GetName();
66 }
67
GetCurrentStateId() const68 StateId StateMachine::GetCurrentStateId() const
69 {
70 return curState_->GetStateId();
71 }
72
SendEvent(Intent intent,const Plugin::Any & param) const73 ErrorCode StateMachine::SendEvent(Intent intent, const Plugin::Any& param) const
74 {
75 return const_cast<StateMachine*>(this)->SendEvent(intent, param);
76 }
77
SendEvent(Intent intent,const Plugin::Any & param)78 ErrorCode StateMachine::SendEvent(Intent intent, const Plugin::Any& param)
79 {
80 constexpr int timeoutMs = 30000;
81 ErrorCode errorCode = ErrorCode::ERROR_TIMED_OUT;
82 if (!intentSync_.WaitFor(
83 intent, [this, intent, param] { return SendEventAsync(intent, param) == ErrorCode::SUCCESS; },
84 timeoutMs, errorCode)) {
85 MEDIA_LOG_E("SendEvent timeout, intent: " PUBLIC_LOG_S " - " PUBLIC_LOG_D32,
86 State::GetIntentName(intent), static_cast<int>(intent));
87 }
88 return errorCode;
89 }
90
SendEventAsync(Intent intent,const Plugin::Any & param) const91 ErrorCode StateMachine::SendEventAsync(Intent intent, const Plugin::Any& param) const
92 {
93 return const_cast<StateMachine*>(this)->SendEventAsync(intent, param);
94 }
95
SendEventAsync(Intent intent,const Plugin::Any & param)96 ErrorCode StateMachine::SendEventAsync(Intent intent, const Plugin::Any& param)
97 {
98 MEDIA_LOG_D("SendEventAsync, intent: " PUBLIC_LOG_S " - " PUBLIC_LOG_D32,
99 State::GetIntentName(intent), static_cast<int>(intent));
100 if (jobs_.Push([this, intent, param]() -> Action { return ProcessIntent(intent, param); })) {
101 return ErrorCode::SUCCESS;
102 }
103 return ErrorCode::ERROR_UNKNOWN;
104 }
105
ProcessIntent(Intent intent,const Plugin::Any & param)106 Action StateMachine::ProcessIntent(Intent intent, const Plugin::Any& param)
107 {
108 MEDIA_LOG_D("ProcessIntent, curState: " PUBLIC_LOG_S ", intent: " PUBLIC_LOG_S,
109 curState_->GetName().c_str(), State::GetIntentName(intent));
110 PROFILE_BEGIN("ProcessIntent, curState: " PUBLIC_LOG_S ", intent: " PUBLIC_LOG_S,
111 curState_->GetName().c_str(), State::GetIntentName(intent));
112 OSAL::ScopedLock lock(mutex_);
113 lastIntent = intent;
114 ErrorCode rtv = ErrorCode::SUCCESS;
115 Action nextAction = Action::ACTION_BUTT;
116 std::tie(rtv, nextAction) = curState_->Execute(intent, param);
117 if (nextAction != Action::ACTION_BUTT) {
118 if (rtv == ErrorCode::SUCCESS) {
119 rtv = ProcAction(nextAction);
120 } else {
121 (void)ProcAction(nextAction);
122 }
123 }
124 OnIntentExecuted(intent, nextAction, rtv);
125 PROFILE_END("ProcessIntent, curState: " PUBLIC_LOG_S ", intent: " PUBLIC_LOG_S,
126 curState_->GetName().c_str(), State::GetIntentName(intent));
127 return (rtv == ErrorCode::SUCCESS) ? nextAction : Action::ACTION_BUTT;
128 }
129
DoTask()130 void StateMachine::DoTask()
131 {
132 #ifdef UNIT_TEST
133 constexpr int timeoutMs = 500;
134 auto job = jobs_.Pop(timeoutMs);
135 #else
136 auto job = jobs_.Pop();
137 #endif
138 if (!job) {
139 return;
140 }
141 auto action = job();
142 switch (action) {
143 case Action::ACTION_PENDING:
144 pendingJobs_.push(job);
145 break;
146 case Action::TRANS_TO_IDLE:
147 case Action::TRANS_TO_INIT:
148 case Action::TRANS_TO_PREPARING:
149 case Action::TRANS_TO_READY:
150 case Action::TRANS_TO_PLAYING:
151 case Action::TRANS_TO_PAUSE:
152 case Action::TRANS_TO_STOPPED:
153 case Action::TRANS_TO_EOS: {
154 if (!pendingJobs_.empty()) {
155 job = pendingJobs_.front();
156 pendingJobs_.pop();
157 action = job();
158 if (action == Action::ACTION_PENDING) {
159 pendingJobs_.push(job);
160 }
161 }
162 break;
163 }
164 case Action::ACTION_BUTT:
165 // fall through
166 default:
167 break;
168 }
169 }
170
AddState(const std::shared_ptr<State> & state)171 void StateMachine::AddState(const std::shared_ptr<State>& state)
172 {
173 states_[state->GetStateId()] = state;
174 }
175
ProcAction(Action nextAction)176 ErrorCode StateMachine::ProcAction(Action nextAction)
177 {
178 std::shared_ptr<State> nextState = nullptr;
179 switch (nextAction) {
180 case Action::TRANS_TO_IDLE:
181 nextState = states_[StateId::IDLE];
182 break;
183 case Action::TRANS_TO_INIT:
184 nextState = states_[StateId::INIT];
185 break;
186 case Action::TRANS_TO_PREPARING:
187 nextState = states_[StateId::PREPARING];
188 break;
189 case Action::TRANS_TO_READY:
190 nextState = states_[StateId::READY];
191 break;
192 case Action::TRANS_TO_PLAYING:
193 nextState = states_[StateId::PLAYING];
194 break;
195 case Action::TRANS_TO_PAUSE:
196 nextState = states_[StateId::PAUSE];
197 break;
198 case Action::TRANS_TO_STOPPED:
199 nextState = states_[StateId::STOPPED];
200 break;
201 case Action::TRANS_TO_EOS:
202 nextState = states_[StateId::EOS];
203 break;
204 default:
205 break;
206 }
207 ErrorCode ret = ErrorCode::SUCCESS;
208 if (nextState) {
209 ret = TransitionTo(nextState);
210 }
211 return ret;
212 }
213
TransitionTo(const std::shared_ptr<State> & state)214 ErrorCode StateMachine::TransitionTo(const std::shared_ptr<State>& state)
215 {
216 if (state == nullptr) {
217 MEDIA_LOG_E("TransitionTo, state is nullptr");
218 return ErrorCode::ERROR_INVALID_PARAMETER_VALUE;
219 }
220 ErrorCode rtv = ErrorCode::SUCCESS;
221 if (state != curState_) {
222 curState_->Exit();
223 curState_ = state;
224 Action nextAction;
225 std::tie(rtv, nextAction) = curState_->Enter(lastIntent);
226 if (rtv == ErrorCode::SUCCESS) {
227 rtv = ProcAction(nextAction);
228 }
229 if (callback_) {
230 callback_->OnStateChanged(curState_->GetStateId());
231 }
232 }
233 return rtv;
234 }
235
OnIntentExecuted(Intent intent,Action action,ErrorCode result)236 void StateMachine::OnIntentExecuted(Intent intent, Action action, ErrorCode result)
237 {
238 MEDIA_LOG_D("OnIntentExecuted, curState: " PUBLIC_LOG_S ", intent: " PUBLIC_LOG_S ", action: " PUBLIC_LOG_S
239 ", result: " PUBLIC_LOG_S, curState_->GetName().c_str(),
240 State::GetIntentName(intent), State::GetActionName(action), GetErrorName(result));
241 if (action == Action::ACTION_PENDING) {
242 return;
243 }
244 if (result == ErrorCode::ERROR_NO_NOTIFY) {
245 return;
246 }
247 if (intent == Intent::NOTIFY_READY && action == Action::TRANS_TO_PLAYING) {
248 intentSync_.Notify(Intent::PLAY, result);
249 } else {
250 intentSync_.Notify(intent, result);
251 }
252 }
253
Notify(Intent intent,ErrorCode code)254 void StateMachine::Notify(Intent intent, ErrorCode code)
255 {
256 intentSync_.Notify(intent, code);
257 }
258 } // namespace Media
259 } // namespace OHOS