• 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/host_server.h"
18 
19 #include <functional>
20 #include <memory>
21 #include <optional>
22 #include <tuple>
23 
24 #include "common/libs/confui/confui.h"
25 #include "common/libs/fs/shared_buf.h"
26 #include "host/libs/config/cuttlefish_config.h"
27 #include "host/libs/confui/host_utils.h"
28 #include "host/libs/confui/secure_input.h"
29 
30 namespace cuttlefish {
31 namespace confui {
32 namespace {
33 
34 template <typename Derived, typename Base>
DowncastTo(std::unique_ptr<Base> && base)35 std::unique_ptr<Derived> DowncastTo(std::unique_ptr<Base>&& base) {
36   Base* tmp = base.release();
37   Derived* derived = static_cast<Derived*>(tmp);
38   return std::unique_ptr<Derived>(derived);
39 }
40 
41 }  // namespace
42 
43 /**
44  * null if not user/touch, or wrap it and ConfUiSecure{Selection,Touch}Message
45  *
46  * ConfUiMessage must NOT ConfUiSecure{Selection,Touch}Message types
47  */
WrapWithSecureFlag(std::unique_ptr<ConfUiMessage> && base_msg,const bool secure)48 static std::unique_ptr<ConfUiMessage> WrapWithSecureFlag(
49     std::unique_ptr<ConfUiMessage>&& base_msg, const bool secure) {
50   switch (base_msg->GetType()) {
51     case ConfUiCmd::kUserInputEvent: {
52       auto as_selection =
53           DowncastTo<ConfUiUserSelectionMessage>(std::move(base_msg));
54       return ToSecureSelectionMessage(std::move(as_selection), secure);
55     }
56     case ConfUiCmd::kUserTouchEvent: {
57       auto as_touch = DowncastTo<ConfUiUserTouchMessage>(std::move(base_msg));
58       return ToSecureTouchMessage(std::move(as_touch), secure);
59     }
60     default:
61       return nullptr;
62   }
63 }
64 
HostServer(HostModeCtrl & host_mode_ctrl,ConfUiRenderer & host_renderer,const PipeConnectionPair & fd_pair)65 HostServer::HostServer(HostModeCtrl& host_mode_ctrl,
66                        ConfUiRenderer& host_renderer,
67                        const PipeConnectionPair& fd_pair)
68     : display_num_(0),
69       host_renderer_{host_renderer},
70       host_mode_ctrl_(host_mode_ctrl),
71       from_guest_fifo_fd_(fd_pair.from_guest_),
72       to_guest_fifo_fd_(fd_pair.to_guest_) {
73   const size_t max_elements = 20;
74   auto ignore_new =
__anond25b145a0202(ThreadSafeQueue<std::unique_ptr<ConfUiMessage>>::QueueImpl*) 75       [](ThreadSafeQueue<std::unique_ptr<ConfUiMessage>>::QueueImpl*) {
76         // no op, so the queue is still full, and the new item will be discarded
77         return;
78       };
79   hal_cmd_q_id_ = input_multiplexer_.RegisterQueue(
80       HostServer::Multiplexer::CreateQueue(max_elements, ignore_new));
81   user_input_evt_q_id_ = input_multiplexer_.RegisterQueue(
82       HostServer::Multiplexer::CreateQueue(max_elements, ignore_new));
83 }
84 
IsVirtioConsoleOpen() const85 bool HostServer::IsVirtioConsoleOpen() const {
86   return from_guest_fifo_fd_->IsOpen() && to_guest_fifo_fd_->IsOpen();
87 }
88 
CheckVirtioConsole()89 bool HostServer::CheckVirtioConsole() {
90   if (IsVirtioConsoleOpen()) return true;
91   ConfUiLog(FATAL) << "Virtio console is not open";
92   return false;
93 }
94 
Start()95 void HostServer::Start() {
96   if (!CheckVirtioConsole()) {
97     return;
98   }
99   auto hal_cmd_fetching = [this]() { this->HalCmdFetcherLoop(); };
100   auto main = [this]() { this->MainLoop(); };
101   hal_input_fetcher_thread_ =
102       thread::RunThread("HalInputLoop", hal_cmd_fetching);
103   main_loop_thread_ = thread::RunThread("MainLoop", main);
104   ConfUiLog(DEBUG) << "host service started.";
105   return;
106 }
107 
HalCmdFetcherLoop()108 void HostServer::HalCmdFetcherLoop() {
109   while (true) {
110     if (!CheckVirtioConsole()) {
111       return;
112     }
113     auto msg = RecvConfUiMsg(from_guest_fifo_fd_);
114     if (!msg) {
115       ConfUiLog(ERROR) << "Error in RecvConfUiMsg from HAL";
116       // TODO(kwstephenkim): error handling
117       // either file is not open, or ill-formatted message
118       continue;
119     }
120     /*
121      * In case of Vts test, the msg could be a user input. For now, we do not
122      * enforce the input grace period for Vts. However, if ever we do, here is
123      * where the time point check should happen. Once it is enqueued, it is not
124      * always guaranteed to be picked up reasonably soon.
125      */
126     constexpr bool is_secure = false;
127     auto to_override_if_user_input =
128         WrapWithSecureFlag(std::move(msg), is_secure);
129     if (to_override_if_user_input) {
130       msg = std::move(to_override_if_user_input);
131     }
132     input_multiplexer_.Push(hal_cmd_q_id_, std::move(msg));
133   }
134 }
135 
136 /**
137  * Send inputs generated not by auto-tester but by the human users
138  *
139  * Send such inputs into the command queue consumed by the state machine
140  * in the main loop/current session.
141  */
SendUserSelection(std::unique_ptr<ConfUiMessage> & input)142 void HostServer::SendUserSelection(std::unique_ptr<ConfUiMessage>& input) {
143   if (!curr_session_ ||
144       curr_session_->GetState() != MainLoopState::kInSession ||
145       !curr_session_->IsReadyForUserInput()) {
146     // ignore
147     return;
148   }
149   constexpr bool is_secure = true;
150   auto secure_input = WrapWithSecureFlag(std::move(input), is_secure);
151   input_multiplexer_.Push(user_input_evt_q_id_, std::move(secure_input));
152 }
153 
TouchEvent(const int x,const int y,const bool is_down)154 void HostServer::TouchEvent(const int x, const int y, const bool is_down) {
155   if (!is_down || !curr_session_) {
156     return;
157   }
158   std::unique_ptr<ConfUiMessage> input =
159       std::make_unique<ConfUiUserTouchMessage>(GetCurrentSessionId(), x, y);
160   SendUserSelection(input);
161 }
162 
UserAbortEvent()163 void HostServer::UserAbortEvent() {
164   if (!curr_session_) {
165     return;
166   }
167   std::unique_ptr<ConfUiMessage> input =
168       std::make_unique<ConfUiUserSelectionMessage>(GetCurrentSessionId(),
169                                                    UserResponse::kUserAbort);
170   SendUserSelection(input);
171 }
172 
173 // read the comments in the header file
MainLoop()174 [[noreturn]] void HostServer::MainLoop() {
175   while (true) {
176     // this gets one input from either queue:
177     // from HAL or from all webrtc clients
178     // if no input, sleep until there is
179     auto input_ptr = input_multiplexer_.Pop();
180     auto& input = *input_ptr;
181     const auto session_id = input.GetSessionId();
182     const auto cmd = input.GetType();
183     const std::string cmd_str(ToString(cmd));
184 
185     // take input for the Finite States Machine below
186     std::string src = input.IsUserInput() ? "input" : "hal";
187     ConfUiLog(VERBOSE) << "In Session " << GetCurrentSessionId() << ", "
188                        << "in state " << GetCurrentState() << ", "
189                        << "received input from " << src << " cmd =" << cmd_str
190                        << " going to session " << session_id;
191 
192     if (!curr_session_) {
193       if (cmd != ConfUiCmd::kStart) {
194         ConfUiLog(VERBOSE) << ToString(cmd) << " to " << session_id
195                            << " is ignored as there is no session to receive";
196         continue;
197       }
198       // the session is created as kInit
199       curr_session_ = CreateSession(input.GetSessionId());
200     }
201     if (cmd == ConfUiCmd::kUserTouchEvent) {
202       ConfUiSecureUserTouchMessage& touch_event =
203           static_cast<ConfUiSecureUserTouchMessage&>(input);
204       auto [x, y] = touch_event.GetLocation();
205       const bool is_confirm = curr_session_->IsConfirm(x, y);
206       const bool is_cancel = curr_session_->IsCancel(x, y);
207       ConfUiLog(INFO) << "Touch at [" << x << ", " << y << "] was "
208                       << (is_cancel ? "CANCEL"
209                                     : (is_confirm ? "CONFIRM" : "INVALID"));
210       if (!is_confirm && !is_cancel) {
211         // ignore, take the next input
212         continue;
213       }
214       decltype(input_ptr) tmp_input_ptr =
215           std::make_unique<ConfUiUserSelectionMessage>(
216               GetCurrentSessionId(),
217               (is_confirm ? UserResponse::kConfirm : UserResponse::kCancel));
218       input_ptr =
219           WrapWithSecureFlag(std::move(tmp_input_ptr), touch_event.IsSecure());
220     }
221     Transition(input_ptr);
222 
223     // finalize
224     if (curr_session_ &&
225         curr_session_->GetState() == MainLoopState::kAwaitCleanup) {
226       curr_session_->CleanUp();
227       curr_session_ = nullptr;
228     }
229   }  // end of the infinite while loop
230 }
231 
CreateSession(const std::string & name)232 std::shared_ptr<Session> HostServer::CreateSession(const std::string& name) {
233   return std::make_shared<Session>(name, display_num_, host_renderer_,
234                                    host_mode_ctrl_);
235 }
236 
IsUserAbort(ConfUiMessage & msg)237 static bool IsUserAbort(ConfUiMessage& msg) {
238   if (msg.GetType() != ConfUiCmd::kUserInputEvent) {
239     return false;
240   }
241   ConfUiUserSelectionMessage& selection =
242       static_cast<ConfUiUserSelectionMessage&>(msg);
243   return (selection.GetResponse() == UserResponse::kUserAbort);
244 }
245 
Transition(std::unique_ptr<ConfUiMessage> & input_ptr)246 void HostServer::Transition(std::unique_ptr<ConfUiMessage>& input_ptr) {
247   auto& input = *input_ptr;
248   const auto session_id = input.GetSessionId();
249   const auto cmd = input.GetType();
250   const std::string cmd_str(ToString(cmd));
251   FsmInput fsm_input = ToFsmInput(input);
252   ConfUiLog(VERBOSE) << "Handling " << ToString(cmd);
253   if (IsUserAbort(input)) {
254     curr_session_->UserAbort(to_guest_fifo_fd_);
255     return;
256   }
257 
258   if (cmd == ConfUiCmd::kAbort) {
259     curr_session_->Abort();
260     return;
261   }
262   curr_session_->Transition(to_guest_fifo_fd_, fsm_input, input);
263 }
264 
265 }  // end of namespace confui
266 }  // end of namespace cuttlefish
267