• 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 #include <algorithm>
20 
21 #include "host/libs/confui/secure_input.h"
22 
23 namespace cuttlefish {
24 namespace confui {
25 
Session(const std::string & session_name,const std::uint32_t display_num,HostModeCtrl & host_mode_ctrl,ScreenConnectorFrameRenderer & screen_connector,const std::string & locale)26 Session::Session(const std::string& session_name,
27                  const std::uint32_t display_num, HostModeCtrl& host_mode_ctrl,
28                  ScreenConnectorFrameRenderer& screen_connector,
29                  const std::string& locale)
30     : session_id_{session_name},
31       display_num_{display_num},
32       host_mode_ctrl_{host_mode_ctrl},
33       screen_connector_{screen_connector},
34       locale_{locale},
35       state_{MainLoopState::kInit},
36       saved_state_{MainLoopState::kInit} {}
37 
38 /** return grace period + alpha
39  *
40  * grace period is the gap between user seeing the dialog
41  * and the UI starts to take the user inputs
42  * Grace period should be at least 1s.
43  * Session requests the Renderer to render the dialog,
44  * but it might not be immediate. So, add alpha to 1s
45  */
GetGracePeriod()46 static const std::chrono::milliseconds GetGracePeriod() {
47   using std::literals::chrono_literals::operator""ms;
48   return 1000ms + 100ms;
49 }
50 
IsReadyForUserInput() const51 bool Session::IsReadyForUserInput() const {
52   using std::literals::chrono_literals::operator""ms;
53   if (!start_time_) {
54     return false;
55   }
56   const auto right_now = Clock::now();
57   return (right_now - *start_time_) >= GetGracePeriod();
58 }
59 
IsConfUiActive() const60 bool Session::IsConfUiActive() const {
61   if (state_ == MainLoopState::kInSession ||
62       state_ == MainLoopState::kWaitStop) {
63     return true;
64   }
65   return false;
66 }
67 
68 template <typename C, typename T>
Contains(const C & c,T && item)69 static bool Contains(const C& c, T&& item) {
70   auto itr = std::find(c.begin(), c.end(), std::forward<T>(item));
71   return itr != c.end();
72 }
73 
IsInverted() const74 bool Session::IsInverted() const {
75   return Contains(ui_options_, teeui::UIOption::AccessibilityInverted);
76 }
77 
IsMagnified() const78 bool Session::IsMagnified() const {
79   return Contains(ui_options_, teeui::UIOption::AccessibilityMagnified);
80 }
81 
RenderDialog()82 bool Session::RenderDialog() {
83   renderer_ = ConfUiRenderer::GenerateRenderer(
84       display_num_, prompt_text_, locale_, IsInverted(), IsMagnified());
85   if (!renderer_) {
86     return false;
87   }
88   auto teeui_frame = renderer_->RenderRawFrame();
89   if (!teeui_frame) {
90     return false;
91   }
92   ConfUiLog(VERBOSE) << "actually trying to render the frame"
93                      << thread::GetName();
94   auto frame_width = teeui_frame->Width();
95   auto frame_height = teeui_frame->Height();
96   auto frame_stride_bytes = teeui_frame->ScreenStrideBytes();
97   auto frame_bytes = reinterpret_cast<std::uint8_t*>(teeui_frame->data());
98   return screen_connector_.RenderConfirmationUi(
99       display_num_, frame_width, frame_height, frame_stride_bytes, frame_bytes);
100 }
101 
Transition(SharedFD & hal_cli,const FsmInput fsm_input,const ConfUiMessage & conf_ui_message)102 MainLoopState Session::Transition(SharedFD& hal_cli, const FsmInput fsm_input,
103                                   const ConfUiMessage& conf_ui_message) {
104   bool should_keep_running = false;
105   bool already_terminated = false;
106   switch (state_) {
107     case MainLoopState::kInit: {
108       should_keep_running = HandleInit(hal_cli, fsm_input, conf_ui_message);
109     } break;
110     case MainLoopState::kInSession: {
111       should_keep_running =
112           HandleInSession(hal_cli, fsm_input, conf_ui_message);
113     } break;
114     case MainLoopState::kWaitStop: {
115       if (IsUserInput(fsm_input)) {
116         ConfUiLog(VERBOSE) << "User input ignored " << ToString(fsm_input)
117                            << " : " << ToString(conf_ui_message)
118                            << " at the state " << ToString(state_);
119       }
120       should_keep_running = HandleWaitStop(hal_cli, fsm_input);
121     } break;
122     case MainLoopState::kTerminated: {
123       already_terminated = true;
124     } break;
125     default:
126       ConfUiLog(FATAL) << "Must not be in the state of " << ToString(state_);
127       break;
128   }
129   if (!should_keep_running && !already_terminated) {
130     ScheduleToTerminate();
131   }
132   return state_;
133 };
134 
CleanUp()135 void Session::CleanUp() {
136   if (state_ != MainLoopState::kAwaitCleanup) {
137     ConfUiLog(FATAL) << "Clean up a session only when in kAwaitCleanup";
138   }
139   state_ = MainLoopState::kTerminated;
140   // common action done when the state is back to init state
141   host_mode_ctrl_.SetMode(HostModeCtrl::ModeType::kAndroidMode);
142 }
143 
ScheduleToTerminate()144 void Session::ScheduleToTerminate() {
145   state_ = MainLoopState::kAwaitCleanup;
146   saved_state_ = MainLoopState::kInvalid;
147 }
148 
ReportErrorToHal(SharedFD hal_cli,const std::string & msg)149 bool Session::ReportErrorToHal(SharedFD hal_cli, const std::string& msg) {
150   ScheduleToTerminate();
151   if (!SendAck(hal_cli, session_id_, false, msg)) {
152     ConfUiLog(ERROR) << "I/O error in sending ack to report rendering failure";
153     return false;
154   }
155   return true;
156 }
157 
Abort()158 void Session::Abort() {
159   ConfUiLog(VERBOSE) << "Abort is called";
160   ScheduleToTerminate();
161   return;
162 }
163 
UserAbort(SharedFD hal_cli)164 void Session::UserAbort(SharedFD hal_cli) {
165   ConfUiLog(VERBOSE) << "it is a user abort input.";
166   SendAbortCmd(hal_cli, GetId());
167   Abort();
168   ScheduleToTerminate();
169 }
170 
HandleInit(SharedFD hal_cli,const FsmInput fsm_input,const ConfUiMessage & conf_ui_message)171 bool Session::HandleInit(SharedFD hal_cli, const FsmInput fsm_input,
172                          const ConfUiMessage& conf_ui_message) {
173   if (IsUserInput(fsm_input)) {
174     // ignore user input
175     state_ = MainLoopState::kInit;
176     return true;
177   }
178 
179   ConfUiLog(VERBOSE) << ToString(fsm_input) << "is handled in HandleInit";
180   if (fsm_input != FsmInput::kHalStart) {
181     ConfUiLog(ERROR) << "invalid cmd for Init State:" << ToString(fsm_input);
182     // ReportErrorToHal returns true if error report was successful
183     // However, anyway we abort this session on the host
184     ReportErrorToHal(hal_cli, HostError::kSystemError);
185     return false;
186   }
187 
188   // Start Session
189   ConfUiLog(VERBOSE) << "Sending ack to hal_cli: "
190                      << Enum2Base(ConfUiCmd::kCliAck);
191   host_mode_ctrl_.SetMode(HostModeCtrl::ModeType::kConfUI_Mode);
192 
193   auto start_cmd_msg = static_cast<const ConfUiStartMessage&>(conf_ui_message);
194   prompt_text_ = start_cmd_msg.GetPromptText();
195   locale_ = start_cmd_msg.GetLocale();
196   extra_data_ = start_cmd_msg.GetExtraData();
197   ui_options_ = start_cmd_msg.GetUiOpts();
198 
199   // cbor_ can be correctly created after the session received kStart cmd
200   // at runtime
201   cbor_ = std::make_unique<Cbor>(prompt_text_, extra_data_);
202   if (cbor_->IsMessageTooLong()) {
203     ConfUiLog(ERROR) << "The prompt text and extra_data are too long to be "
204                      << "properly encoded.";
205     ReportErrorToHal(hal_cli, HostError::kMessageTooLongError);
206     return false;
207   }
208   if (cbor_->IsMalformedUtf8()) {
209     ConfUiLog(ERROR) << "The prompt text appears to have incorrect UTF8 format";
210     ReportErrorToHal(hal_cli, HostError::kIncorrectUTF8);
211     return false;
212   }
213   if (!cbor_->IsOk()) {
214     ConfUiLog(ERROR) << "Unknown Error in cbor implementation";
215     ReportErrorToHal(hal_cli, HostError::kSystemError);
216     return false;
217   }
218 
219   if (!RenderDialog()) {
220     // the confirmation UI is driven by a user app, not running from the start
221     // automatically so that means webRTC should have been set up
222     ConfUiLog(ERROR) << "Dialog is not rendered. However, it should."
223                      << "No webRTC can't initiate any confirmation UI.";
224     ReportErrorToHal(hal_cli, HostError::kUIError);
225     return false;
226   }
227   start_time_ = std::make_unique<TimePoint>(std::move(Clock::now()));
228   if (!SendAck(hal_cli, session_id_, true, "started")) {
229     ConfUiLog(ERROR) << "Ack to kStart failed in I/O";
230     return false;
231   }
232   state_ = MainLoopState::kInSession;
233   return true;
234 }
235 
HandleInSession(SharedFD hal_cli,const FsmInput fsm_input,const ConfUiMessage & conf_ui_msg)236 bool Session::HandleInSession(SharedFD hal_cli, const FsmInput fsm_input,
237                               const ConfUiMessage& conf_ui_msg) {
238   auto invalid_input_handler = [&, this]() {
239     ReportErrorToHal(hal_cli, HostError::kSystemError);
240     ConfUiLog(ERROR) << "cmd " << ToString(fsm_input)
241                      << " should not be handled in HandleInSession";
242   };
243 
244   if (!IsUserInput(fsm_input)) {
245     invalid_input_handler();
246     return false;
247   }
248 
249   const auto& user_input_msg =
250       static_cast<const ConfUiSecureUserSelectionMessage&>(conf_ui_msg);
251   const auto response = user_input_msg.GetResponse();
252   if (response == UserResponse::kUnknown ||
253       response == UserResponse::kUserAbort) {
254     invalid_input_handler();
255     return false;
256   }
257   const bool is_secure_input = user_input_msg.IsSecure();
258 
259   ConfUiLog(VERBOSE) << "In HandleInSession, session " << session_id_
260                      << " is sending the user input " << ToString(fsm_input);
261 
262   bool is_success = false;
263   if (response == UserResponse::kCancel) {
264     // no need to sign
265     is_success =
266         SendResponse(hal_cli, session_id_, UserResponse::kCancel,
267                      std::vector<std::uint8_t>{}, std::vector<std::uint8_t>{});
268   } else {
269     message_ = std::move(cbor_->GetMessage());
270     auto message_opt = (is_secure_input ? Sign(message_) : TestSign(message_));
271     if (!message_opt) {
272       ReportErrorToHal(hal_cli, HostError::kSystemError);
273       return false;
274     }
275     signed_confirmation_ = message_opt.value();
276     is_success = SendResponse(hal_cli, session_id_, UserResponse::kConfirm,
277                               signed_confirmation_, message_);
278   }
279 
280   if (!is_success) {
281     ConfUiLog(ERROR) << "I/O error in sending user response to HAL";
282     return false;
283   }
284   state_ = MainLoopState::kWaitStop;
285   return true;
286 }
287 
HandleWaitStop(SharedFD hal_cli,const FsmInput fsm_input)288 bool Session::HandleWaitStop(SharedFD hal_cli, const FsmInput fsm_input) {
289   if (IsUserInput(fsm_input)) {
290     // ignore user input
291     state_ = MainLoopState::kWaitStop;
292     return true;
293   }
294   if (fsm_input == FsmInput::kHalStop) {
295     ConfUiLog(VERBOSE) << "Handling Abort in kWaitStop.";
296     ScheduleToTerminate();
297     return true;
298   }
299   ReportErrorToHal(hal_cli, HostError::kSystemError);
300   ConfUiLog(FATAL) << "In WaitStop, received wrong HAL command "
301                    << ToString(fsm_input);
302   return false;
303 }
304 
305 }  // end of namespace confui
306 }  // end of namespace cuttlefish
307