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