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