1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "host/libs/confui/session.h"
18
19 namespace cuttlefish {
20 namespace confui {
21
Session(const std::string & session_id,const std::uint32_t display_num,ConfUiRenderer & host_renderer,HostModeCtrl & host_mode_ctrl,ScreenConnectorFrameRenderer & screen_connector,const std::string & locale)22 Session::Session(const std::string& session_id, const std::uint32_t display_num,
23 ConfUiRenderer& host_renderer, HostModeCtrl& host_mode_ctrl,
24 ScreenConnectorFrameRenderer& screen_connector,
25 const std::string& locale)
26 : session_id_{session_id},
27 display_num_{display_num},
28 renderer_{host_renderer},
29 host_mode_ctrl_{host_mode_ctrl},
30 screen_connector_{screen_connector},
31 locale_{locale},
32 state_{MainLoopState::kInit},
33 saved_state_{MainLoopState::kInit} {}
34
IsConfUiActive() const35 bool Session::IsConfUiActive() const {
36 if (state_ == MainLoopState::kInSession ||
37 state_ == MainLoopState::kWaitStop) {
38 return true;
39 }
40 return false;
41 }
42
RenderDialog(const std::string & msg,const std::string & locale)43 bool Session::RenderDialog(const std::string& msg, const std::string& locale) {
44 auto [teeui_frame, is_success] = renderer_.RenderRawFrame(msg, locale);
45 if (!is_success) {
46 return false;
47 }
48 prompt_ = msg;
49 locale_ = locale;
50
51 ConfUiLog(DEBUG) << "actually trying to render the frame"
52 << thread::GetName();
53 auto raw_frame = reinterpret_cast<std::uint8_t*>(teeui_frame.data());
54 return screen_connector_.RenderConfirmationUi(display_num_, raw_frame);
55 }
56
IsSuspended() const57 bool Session::IsSuspended() const {
58 return (state_ == MainLoopState::kSuspended);
59 }
60
Transition(const bool is_user_input,SharedFD & hal_cli,const FsmInput fsm_input,const std::string & additional_info)61 MainLoopState Session::Transition(const bool is_user_input, SharedFD& hal_cli,
62 const FsmInput fsm_input,
63 const std::string& additional_info) {
64 switch (state_) {
65 case MainLoopState::kInit: {
66 HandleInit(is_user_input, hal_cli, fsm_input, additional_info);
67 } break;
68 case MainLoopState::kInSession: {
69 HandleInSession(is_user_input, hal_cli, fsm_input);
70 } break;
71 case MainLoopState::kWaitStop: {
72 if (is_user_input) {
73 ConfUiLog(DEBUG) << "User input ignored" << ToString(fsm_input) << " : "
74 << additional_info << "at state" << ToString(state_);
75 }
76 HandleWaitStop(is_user_input, hal_cli, fsm_input);
77 } break;
78 default:
79 // host service explicitly calls restore and suspend
80 ConfUiLog(FATAL) << "Must not be in the state of" << ToString(state_);
81 break;
82 }
83 return state_;
84 };
85
Suspend(SharedFD hal_cli)86 bool Session::Suspend(SharedFD hal_cli) {
87 if (state_ == MainLoopState::kInit) {
88 // HAL sent wrong command
89 ConfUiLog(FATAL)
90 << "HAL sent wrong command, suspend, when the session is in kIinit";
91 return false;
92 }
93 if (state_ == MainLoopState::kSuspended) {
94 ConfUiLog(DEBUG) << "Already kSuspended state";
95 return false;
96 }
97 saved_state_ = state_;
98 state_ = MainLoopState::kSuspended;
99 host_mode_ctrl_.SetMode(HostModeCtrl::ModeType::kAndroidMode);
100 if (!packet::SendAck(hal_cli, session_id_, /*is success*/ true,
101 "suspended")) {
102 ConfUiLog(FATAL) << "I/O error";
103 return false;
104 }
105 return true;
106 }
107
Restore(SharedFD hal_cli)108 bool Session::Restore(SharedFD hal_cli) {
109 if (state_ == MainLoopState::kInit) {
110 // HAL sent wrong command
111 ConfUiLog(FATAL)
112 << "HAL sent wrong command, restore, when the session is in kIinit";
113 return false;
114 }
115
116 if (state_ != MainLoopState::kSuspended) {
117 ConfUiLog(DEBUG) << "Already Restored to state " + ToString(state_);
118 return false;
119 }
120 host_mode_ctrl_.SetMode(HostModeCtrl::ModeType::kConfUI_Mode);
121 if (!RenderDialog(prompt_, locale_)) {
122 // the confirmation UI is driven by a user app, not running from the start
123 // automatically so that means webRTC/vnc should have been set up
124 ConfUiLog(ERROR) << "Dialog is not rendered. However, it should."
125 << "No webRTC can't initiate any confirmation UI.";
126 if (!packet::SendAck(hal_cli, session_id_, false,
127 "render failed in restore")) {
128 ConfUiLog(FATAL) << "Rendering failed in restore, and ack failed in I/O";
129 }
130 state_ = MainLoopState::kInit;
131 return false;
132 }
133 if (!packet::SendAck(hal_cli, session_id_, true, "restored")) {
134 ConfUiLog(FATAL) << "Ack to restore failed in I/O";
135 }
136 state_ = saved_state_;
137 saved_state_ = MainLoopState::kInit;
138 return true;
139 }
140
Kill(SharedFD hal_cli,const std::string & response_msg)141 bool Session::Kill(SharedFD hal_cli, const std::string& response_msg) {
142 state_ = MainLoopState::kAwaitCleanup;
143 saved_state_ = MainLoopState::kInvalid;
144 if (!packet::SendAck(hal_cli, session_id_, true, response_msg)) {
145 ConfUiLog(FATAL) << "I/O error in ack to Abort";
146 return false;
147 }
148 return true;
149 }
150
CleanUp()151 void Session::CleanUp() {
152 if (state_ != MainLoopState::kAwaitCleanup) {
153 ConfUiLog(FATAL) << "Clean up a session only when in kAwaitCleanup";
154 }
155 // common action done when the state is back to init state
156 host_mode_ctrl_.SetMode(HostModeCtrl::ModeType::kAndroidMode);
157 }
158
ReportErrorToHal(SharedFD hal_cli,const std::string & msg)159 void Session::ReportErrorToHal(SharedFD hal_cli, const std::string& msg) {
160 // reset the session -- destroy it & recreate it with the same
161 // session id
162 state_ = MainLoopState::kAwaitCleanup;
163 if (!packet::SendAck(hal_cli, session_id_, false, msg)) {
164 ConfUiLog(FATAL) << "I/O error in sending ack to report rendering failure";
165 }
166 return;
167 }
168
Abort(SharedFD hal_cli)169 bool Session::Abort(SharedFD hal_cli) { return Kill(hal_cli, "aborted"); }
170
HandleInit(const bool is_user_input,SharedFD hal_cli,const FsmInput fsm_input,const std::string & additional_info)171 void Session::HandleInit(const bool is_user_input, SharedFD hal_cli,
172 const FsmInput fsm_input,
173 const std::string& additional_info) {
174 using namespace cuttlefish::confui::packet;
175 if (is_user_input) {
176 // ignore user input
177 state_ = MainLoopState::kInit;
178 return;
179 }
180
181 ConfUiLog(DEBUG) << ToString(fsm_input) << "is handled in HandleInit";
182 if (fsm_input != FsmInput::kHalStart) {
183 ConfUiLog(ERROR) << "invalid cmd for Init State:" << ToString(fsm_input);
184 // reset the session -- destroy it & recreate it with the same
185 // session id
186 ReportErrorToHal(hal_cli, "wrong hal command");
187 return;
188 }
189
190 // Start Session
191 ConfUiLog(DEBUG) << "Sending ack to hal_cli: "
192 << Enum2Base(ConfUiCmd::kCliAck);
193 host_mode_ctrl_.SetMode(HostModeCtrl::ModeType::kConfUI_Mode);
194 auto confirmation_msg = additional_info;
195 if (!RenderDialog(confirmation_msg, locale_)) {
196 // the confirmation UI is driven by a user app, not running from the start
197 // automatically so that means webRTC/vnc should have been set up
198 ConfUiLog(ERROR) << "Dialog is not rendered. However, it should."
199 << "No webRTC can't initiate any confirmation UI.";
200 ReportErrorToHal(hal_cli, "rendering failed");
201 return;
202 }
203 if (!packet::SendAck(hal_cli, session_id_, true, "started")) {
204 ConfUiLog(FATAL) << "Ack to kStart failed in I/O";
205 }
206 state_ = MainLoopState::kInSession;
207 return;
208 }
209
HandleInSession(const bool is_user_input,SharedFD hal_cli,const FsmInput fsm_input)210 void Session::HandleInSession(const bool is_user_input, SharedFD hal_cli,
211 const FsmInput fsm_input) {
212 if (!is_user_input) {
213 ConfUiLog(FATAL) << "cmd" << ToString(fsm_input)
214 << "should not be handled in HandleInSession";
215 ReportErrorToHal(hal_cli, "wrong hal command");
216 return;
217 }
218
219 // send to hal_cli either confirm or cancel
220 if (fsm_input != FsmInput::kUserConfirm &&
221 fsm_input != FsmInput::kUserCancel) {
222 /*
223 * TODO(kwstephenkim@google.com): change here when other user inputs must
224 * be handled
225 *
226 */
227 if (!packet::SendAck(hal_cli, session_id_, true,
228 "invalid user input error")) {
229 // note that input is what we control in memory
230 ConfUiCheck(false) << "Input must be either confirm or cancel for now.";
231 }
232 return;
233 }
234
235 ConfUiLog(DEBUG) << "In HandlieInSession, session" << session_id_
236 << "is sending the user input" << ToString(fsm_input);
237 auto selection = UserResponse::kConfirm;
238 if (fsm_input == FsmInput::kUserCancel) {
239 selection = UserResponse::kCancel;
240 }
241 if (!packet::SendResponse(hal_cli, session_id_, selection)) {
242 ConfUiLog(FATAL) << "I/O error in sending user response to HAL";
243 }
244 state_ = MainLoopState::kWaitStop;
245 return;
246 }
247
HandleWaitStop(const bool is_user_input,SharedFD hal_cli,const FsmInput fsm_input)248 void Session::HandleWaitStop(const bool is_user_input, SharedFD hal_cli,
249 const FsmInput fsm_input) {
250 using namespace cuttlefish::confui::packet;
251
252 if (is_user_input) {
253 // ignore user input
254 state_ = MainLoopState::kWaitStop;
255 return;
256 }
257 if (fsm_input == FsmInput::kHalStop) {
258 ConfUiLog(DEBUG) << "Handling Abort in kWaitStop.";
259 Kill(hal_cli, "stopped");
260 return;
261 }
262 ConfUiLog(FATAL) << "In WaitStop, received wrong HAL command "
263 << ToString(fsm_input);
264 state_ = MainLoopState::kAwaitCleanup;
265 return;
266 }
267
268 } // end of namespace confui
269 } // end of namespace cuttlefish
270