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