1fa7767c5Sopenharmony_ci/*
2fa7767c5Sopenharmony_ci * Copyright (c) 2021-2021 Huawei Device Co., Ltd.
3fa7767c5Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4fa7767c5Sopenharmony_ci * you may not use this file except in compliance with the License.
5fa7767c5Sopenharmony_ci * You may obtain a copy of the License at
6fa7767c5Sopenharmony_ci *
7fa7767c5Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8fa7767c5Sopenharmony_ci *
9fa7767c5Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10fa7767c5Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11fa7767c5Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12fa7767c5Sopenharmony_ci * See the License for the specific language governing permissions and
13fa7767c5Sopenharmony_ci * limitations under the License.
14fa7767c5Sopenharmony_ci */
15fa7767c5Sopenharmony_ci
16fa7767c5Sopenharmony_ci#define HST_LOG_TAG "StateMachine"
17fa7767c5Sopenharmony_ci
18fa7767c5Sopenharmony_ci#include "state_machine.h"
19fa7767c5Sopenharmony_ci#include "eos_state.h"
20fa7767c5Sopenharmony_ci#include "foundation/osal/utils/util.h"
21fa7767c5Sopenharmony_ci#include "foundation/utils/steady_clock.h"
22fa7767c5Sopenharmony_ci#include "idle_state.h"
23fa7767c5Sopenharmony_ci#include "init_state.h"
24fa7767c5Sopenharmony_ci#include "pause_state.h"
25fa7767c5Sopenharmony_ci#include "playing_state.h"
26fa7767c5Sopenharmony_ci#include "preparing_state.h"
27fa7767c5Sopenharmony_ci#include "ready_state.h"
28fa7767c5Sopenharmony_ci#include "stopped_state.h"
29fa7767c5Sopenharmony_ci
30fa7767c5Sopenharmony_cinamespace OHOS {
31fa7767c5Sopenharmony_cinamespace Media {
32fa7767c5Sopenharmony_ciStateMachine::StateMachine(PlayExecutor& executor)
33fa7767c5Sopenharmony_ci    : Task("StateMachine"),
34fa7767c5Sopenharmony_ci      intentSync_("fsmSync"),
35fa7767c5Sopenharmony_ci      curState_(std::make_shared<IdleState>(StateId::IDLE, executor)),
36fa7767c5Sopenharmony_ci      jobs_("StateMachineJobQue")
37fa7767c5Sopenharmony_ci{
38fa7767c5Sopenharmony_ci    AddState(curState_);
39fa7767c5Sopenharmony_ci    AddState(std::make_shared<InitState>(StateId::INIT, executor));
40fa7767c5Sopenharmony_ci    AddState(std::make_shared<PreparingState>(StateId::PREPARING, executor));
41fa7767c5Sopenharmony_ci    AddState(std::make_shared<ReadyState>(StateId::READY, executor));
42fa7767c5Sopenharmony_ci    AddState(std::make_shared<PlayingState>(StateId::PLAYING, executor));
43fa7767c5Sopenharmony_ci    AddState(std::make_shared<PauseState>(StateId::PAUSE, executor));
44fa7767c5Sopenharmony_ci    AddState(std::make_shared<StoppedState>(StateId::STOPPED, executor));
45fa7767c5Sopenharmony_ci    AddState(std::make_shared<EosState>(StateId::EOS, executor));
46fa7767c5Sopenharmony_ci}
47fa7767c5Sopenharmony_ci
48fa7767c5Sopenharmony_civoid StateMachine::Stop()
49fa7767c5Sopenharmony_ci{
50fa7767c5Sopenharmony_ci    MEDIA_LOG_I("StateMachine stop called.");
51fa7767c5Sopenharmony_ci    while (!jobs_.Empty()) {
52fa7767c5Sopenharmony_ci        OSAL::SleepFor(10); // 10
53fa7767c5Sopenharmony_ci    }
54fa7767c5Sopenharmony_ci    jobs_.SetActive(false);
55fa7767c5Sopenharmony_ci    Task::Stop();
56fa7767c5Sopenharmony_ci}
57fa7767c5Sopenharmony_ci
58fa7767c5Sopenharmony_civoid StateMachine::SetStateCallback(StateChangeCallback* callback)
59fa7767c5Sopenharmony_ci{
60fa7767c5Sopenharmony_ci    callback_ = callback;
61fa7767c5Sopenharmony_ci}
62fa7767c5Sopenharmony_ci
63fa7767c5Sopenharmony_ciconst std::string& StateMachine::GetCurrentState() const
64fa7767c5Sopenharmony_ci{
65fa7767c5Sopenharmony_ci    return curState_->GetName();
66fa7767c5Sopenharmony_ci}
67fa7767c5Sopenharmony_ci
68fa7767c5Sopenharmony_ciStateId StateMachine::GetCurrentStateId() const
69fa7767c5Sopenharmony_ci{
70fa7767c5Sopenharmony_ci    return curState_->GetStateId();
71fa7767c5Sopenharmony_ci}
72fa7767c5Sopenharmony_ci
73fa7767c5Sopenharmony_ciErrorCode StateMachine::SendEvent(Intent intent, const Plugin::Any& param) const
74fa7767c5Sopenharmony_ci{
75fa7767c5Sopenharmony_ci    return const_cast<StateMachine*>(this)->SendEvent(intent, param);
76fa7767c5Sopenharmony_ci}
77fa7767c5Sopenharmony_ci
78fa7767c5Sopenharmony_ciErrorCode StateMachine::SendEvent(Intent intent, const Plugin::Any& param)
79fa7767c5Sopenharmony_ci{
80fa7767c5Sopenharmony_ci    constexpr int timeoutMs = 30000;
81fa7767c5Sopenharmony_ci    ErrorCode errorCode = ErrorCode::ERROR_TIMED_OUT;
82fa7767c5Sopenharmony_ci    if (!intentSync_.WaitFor(
83fa7767c5Sopenharmony_ci        intent, [this, intent, param] { return SendEventAsync(intent, param) == ErrorCode::SUCCESS; },
84fa7767c5Sopenharmony_ci        timeoutMs, errorCode)) {
85fa7767c5Sopenharmony_ci        MEDIA_LOG_E("SendEvent timeout, intent: " PUBLIC_LOG_S " - " PUBLIC_LOG_D32,
86fa7767c5Sopenharmony_ci                    State::GetIntentName(intent), static_cast<int>(intent));
87fa7767c5Sopenharmony_ci    }
88fa7767c5Sopenharmony_ci    return errorCode;
89fa7767c5Sopenharmony_ci}
90fa7767c5Sopenharmony_ci
91fa7767c5Sopenharmony_ciErrorCode StateMachine::SendEventAsync(Intent intent, const Plugin::Any& param) const
92fa7767c5Sopenharmony_ci{
93fa7767c5Sopenharmony_ci    return const_cast<StateMachine*>(this)->SendEventAsync(intent, param);
94fa7767c5Sopenharmony_ci}
95fa7767c5Sopenharmony_ci
96fa7767c5Sopenharmony_ciErrorCode StateMachine::SendEventAsync(Intent intent, const Plugin::Any& param)
97fa7767c5Sopenharmony_ci{
98fa7767c5Sopenharmony_ci    MEDIA_LOG_D("SendEventAsync, intent: " PUBLIC_LOG_S " - " PUBLIC_LOG_D32,
99fa7767c5Sopenharmony_ci                State::GetIntentName(intent), static_cast<int>(intent));
100fa7767c5Sopenharmony_ci    if (jobs_.Push([this, intent, param]() -> Action { return ProcessIntent(intent, param); })) {
101fa7767c5Sopenharmony_ci        return ErrorCode::SUCCESS;
102fa7767c5Sopenharmony_ci    }
103fa7767c5Sopenharmony_ci    return ErrorCode::ERROR_UNKNOWN;
104fa7767c5Sopenharmony_ci}
105fa7767c5Sopenharmony_ci
106fa7767c5Sopenharmony_ciAction StateMachine::ProcessIntent(Intent intent, const Plugin::Any& param)
107fa7767c5Sopenharmony_ci{
108fa7767c5Sopenharmony_ci    MEDIA_LOG_D("ProcessIntent, curState: " PUBLIC_LOG_S ", intent: " PUBLIC_LOG_S,
109fa7767c5Sopenharmony_ci                curState_->GetName().c_str(), State::GetIntentName(intent));
110fa7767c5Sopenharmony_ci    PROFILE_BEGIN("ProcessIntent, curState: " PUBLIC_LOG_S ", intent: " PUBLIC_LOG_S,
111fa7767c5Sopenharmony_ci                  curState_->GetName().c_str(), State::GetIntentName(intent));
112fa7767c5Sopenharmony_ci    OSAL::ScopedLock lock(mutex_);
113fa7767c5Sopenharmony_ci    lastIntent = intent;
114fa7767c5Sopenharmony_ci    ErrorCode rtv = ErrorCode::SUCCESS;
115fa7767c5Sopenharmony_ci    Action nextAction = Action::ACTION_BUTT;
116fa7767c5Sopenharmony_ci    std::tie(rtv, nextAction) = curState_->Execute(intent, param);
117fa7767c5Sopenharmony_ci    if (nextAction != Action::ACTION_BUTT) {
118fa7767c5Sopenharmony_ci        if (rtv == ErrorCode::SUCCESS) {
119fa7767c5Sopenharmony_ci            rtv = ProcAction(nextAction);
120fa7767c5Sopenharmony_ci        } else {
121fa7767c5Sopenharmony_ci            (void)ProcAction(nextAction);
122fa7767c5Sopenharmony_ci        }
123fa7767c5Sopenharmony_ci    }
124fa7767c5Sopenharmony_ci    OnIntentExecuted(intent, nextAction, rtv);
125fa7767c5Sopenharmony_ci    PROFILE_END("ProcessIntent, curState: " PUBLIC_LOG_S ", intent: " PUBLIC_LOG_S,
126fa7767c5Sopenharmony_ci                curState_->GetName().c_str(), State::GetIntentName(intent));
127fa7767c5Sopenharmony_ci    return (rtv == ErrorCode::SUCCESS) ? nextAction : Action::ACTION_BUTT;
128fa7767c5Sopenharmony_ci}
129fa7767c5Sopenharmony_ci
130fa7767c5Sopenharmony_civoid StateMachine::DoTask()
131fa7767c5Sopenharmony_ci{
132fa7767c5Sopenharmony_ci#ifdef UNIT_TEST
133fa7767c5Sopenharmony_ci    constexpr int timeoutMs = 500;
134fa7767c5Sopenharmony_ci    auto job = jobs_.Pop(timeoutMs);
135fa7767c5Sopenharmony_ci#else
136fa7767c5Sopenharmony_ci    auto job = jobs_.Pop();
137fa7767c5Sopenharmony_ci#endif
138fa7767c5Sopenharmony_ci    if (!job) {
139fa7767c5Sopenharmony_ci        return;
140fa7767c5Sopenharmony_ci    }
141fa7767c5Sopenharmony_ci    auto action = job();
142fa7767c5Sopenharmony_ci    switch (action) {
143fa7767c5Sopenharmony_ci        case Action::ACTION_PENDING:
144fa7767c5Sopenharmony_ci            pendingJobs_.push(job);
145fa7767c5Sopenharmony_ci            break;
146fa7767c5Sopenharmony_ci        case Action::TRANS_TO_IDLE:
147fa7767c5Sopenharmony_ci        case Action::TRANS_TO_INIT:
148fa7767c5Sopenharmony_ci        case Action::TRANS_TO_PREPARING:
149fa7767c5Sopenharmony_ci        case Action::TRANS_TO_READY:
150fa7767c5Sopenharmony_ci        case Action::TRANS_TO_PLAYING:
151fa7767c5Sopenharmony_ci        case Action::TRANS_TO_PAUSE:
152fa7767c5Sopenharmony_ci        case Action::TRANS_TO_STOPPED:
153fa7767c5Sopenharmony_ci        case Action::TRANS_TO_EOS: {
154fa7767c5Sopenharmony_ci            if (!pendingJobs_.empty()) {
155fa7767c5Sopenharmony_ci                job = pendingJobs_.front();
156fa7767c5Sopenharmony_ci                pendingJobs_.pop();
157fa7767c5Sopenharmony_ci                action = job();
158fa7767c5Sopenharmony_ci                if (action == Action::ACTION_PENDING) {
159fa7767c5Sopenharmony_ci                    pendingJobs_.push(job);
160fa7767c5Sopenharmony_ci                }
161fa7767c5Sopenharmony_ci            }
162fa7767c5Sopenharmony_ci            break;
163fa7767c5Sopenharmony_ci        }
164fa7767c5Sopenharmony_ci        case Action::ACTION_BUTT:
165fa7767c5Sopenharmony_ci            // fall through
166fa7767c5Sopenharmony_ci        default:
167fa7767c5Sopenharmony_ci            break;
168fa7767c5Sopenharmony_ci    }
169fa7767c5Sopenharmony_ci}
170fa7767c5Sopenharmony_ci
171fa7767c5Sopenharmony_civoid StateMachine::AddState(const std::shared_ptr<State>& state)
172fa7767c5Sopenharmony_ci{
173fa7767c5Sopenharmony_ci    states_[state->GetStateId()] = state;
174fa7767c5Sopenharmony_ci}
175fa7767c5Sopenharmony_ci
176fa7767c5Sopenharmony_ciErrorCode StateMachine::ProcAction(Action nextAction)
177fa7767c5Sopenharmony_ci{
178fa7767c5Sopenharmony_ci    std::shared_ptr<State> nextState = nullptr;
179fa7767c5Sopenharmony_ci    switch (nextAction) {
180fa7767c5Sopenharmony_ci        case Action::TRANS_TO_IDLE:
181fa7767c5Sopenharmony_ci            nextState = states_[StateId::IDLE];
182fa7767c5Sopenharmony_ci            break;
183fa7767c5Sopenharmony_ci        case Action::TRANS_TO_INIT:
184fa7767c5Sopenharmony_ci            nextState = states_[StateId::INIT];
185fa7767c5Sopenharmony_ci            break;
186fa7767c5Sopenharmony_ci        case Action::TRANS_TO_PREPARING:
187fa7767c5Sopenharmony_ci            nextState = states_[StateId::PREPARING];
188fa7767c5Sopenharmony_ci            break;
189fa7767c5Sopenharmony_ci        case Action::TRANS_TO_READY:
190fa7767c5Sopenharmony_ci            nextState = states_[StateId::READY];
191fa7767c5Sopenharmony_ci            break;
192fa7767c5Sopenharmony_ci        case Action::TRANS_TO_PLAYING:
193fa7767c5Sopenharmony_ci            nextState = states_[StateId::PLAYING];
194fa7767c5Sopenharmony_ci            break;
195fa7767c5Sopenharmony_ci        case Action::TRANS_TO_PAUSE:
196fa7767c5Sopenharmony_ci            nextState = states_[StateId::PAUSE];
197fa7767c5Sopenharmony_ci            break;
198fa7767c5Sopenharmony_ci        case Action::TRANS_TO_STOPPED:
199fa7767c5Sopenharmony_ci            nextState = states_[StateId::STOPPED];
200fa7767c5Sopenharmony_ci            break;
201fa7767c5Sopenharmony_ci        case Action::TRANS_TO_EOS:
202fa7767c5Sopenharmony_ci            nextState = states_[StateId::EOS];
203fa7767c5Sopenharmony_ci            break;
204fa7767c5Sopenharmony_ci        default:
205fa7767c5Sopenharmony_ci            break;
206fa7767c5Sopenharmony_ci    }
207fa7767c5Sopenharmony_ci    ErrorCode ret = ErrorCode::SUCCESS;
208fa7767c5Sopenharmony_ci    if (nextState) {
209fa7767c5Sopenharmony_ci        ret = TransitionTo(nextState);
210fa7767c5Sopenharmony_ci    }
211fa7767c5Sopenharmony_ci    return ret;
212fa7767c5Sopenharmony_ci}
213fa7767c5Sopenharmony_ci
214fa7767c5Sopenharmony_ciErrorCode StateMachine::TransitionTo(const std::shared_ptr<State>& state)
215fa7767c5Sopenharmony_ci{
216fa7767c5Sopenharmony_ci    if (state == nullptr) {
217fa7767c5Sopenharmony_ci        MEDIA_LOG_E("TransitionTo, state is nullptr");
218fa7767c5Sopenharmony_ci        return ErrorCode::ERROR_INVALID_PARAMETER_VALUE;
219fa7767c5Sopenharmony_ci    }
220fa7767c5Sopenharmony_ci    ErrorCode rtv = ErrorCode::SUCCESS;
221fa7767c5Sopenharmony_ci    if (state != curState_) {
222fa7767c5Sopenharmony_ci        curState_->Exit();
223fa7767c5Sopenharmony_ci        curState_ = state;
224fa7767c5Sopenharmony_ci        Action nextAction;
225fa7767c5Sopenharmony_ci        std::tie(rtv, nextAction) = curState_->Enter(lastIntent);
226fa7767c5Sopenharmony_ci        if (rtv == ErrorCode::SUCCESS) {
227fa7767c5Sopenharmony_ci            rtv = ProcAction(nextAction);
228fa7767c5Sopenharmony_ci        }
229fa7767c5Sopenharmony_ci        if (callback_) {
230fa7767c5Sopenharmony_ci            callback_->OnStateChanged(curState_->GetStateId());
231fa7767c5Sopenharmony_ci        }
232fa7767c5Sopenharmony_ci    }
233fa7767c5Sopenharmony_ci    return rtv;
234fa7767c5Sopenharmony_ci}
235fa7767c5Sopenharmony_ci
236fa7767c5Sopenharmony_civoid StateMachine::OnIntentExecuted(Intent intent, Action action, ErrorCode result)
237fa7767c5Sopenharmony_ci{
238fa7767c5Sopenharmony_ci    MEDIA_LOG_D("OnIntentExecuted, curState: " PUBLIC_LOG_S ", intent: " PUBLIC_LOG_S ", action: " PUBLIC_LOG_S
239fa7767c5Sopenharmony_ci                ", result: " PUBLIC_LOG_S, curState_->GetName().c_str(),
240fa7767c5Sopenharmony_ci                State::GetIntentName(intent), State::GetActionName(action), GetErrorName(result));
241fa7767c5Sopenharmony_ci    if (action == Action::ACTION_PENDING) {
242fa7767c5Sopenharmony_ci        return;
243fa7767c5Sopenharmony_ci    }
244fa7767c5Sopenharmony_ci    if (result == ErrorCode::ERROR_NO_NOTIFY) {
245fa7767c5Sopenharmony_ci        return;
246fa7767c5Sopenharmony_ci    }
247fa7767c5Sopenharmony_ci    if (intent == Intent::NOTIFY_READY && action == Action::TRANS_TO_PLAYING) {
248fa7767c5Sopenharmony_ci        intentSync_.Notify(Intent::PLAY, result);
249fa7767c5Sopenharmony_ci    } else {
250fa7767c5Sopenharmony_ci        intentSync_.Notify(intent, result);
251fa7767c5Sopenharmony_ci    }
252fa7767c5Sopenharmony_ci}
253fa7767c5Sopenharmony_ci
254fa7767c5Sopenharmony_civoid StateMachine::Notify(Intent intent, ErrorCode code)
255fa7767c5Sopenharmony_ci{
256fa7767c5Sopenharmony_ci    intentSync_.Notify(intent, code);
257fa7767c5Sopenharmony_ci}
258fa7767c5Sopenharmony_ci} // namespace Media
259fa7767c5Sopenharmony_ci} // namespace OHOS