• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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