• 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 <chrono>
20 #include <functional>
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 {
CuttlefishConfigDefaultInstance()32 static auto CuttlefishConfigDefaultInstance() {
33   auto config = cuttlefish::CuttlefishConfig::Get();
34   CHECK(config) << "Config must not be null";
35   return config->ForDefaultInstance();
36 }
37 
HalHostVsockPort()38 static int HalHostVsockPort() {
39   return CuttlefishConfigDefaultInstance().confui_host_vsock_port();
40 }
41 
42 /**
43  * null if not user/touch, or wrap it and ConfUiSecure{Selection,Touch}Message
44  *
45  * ConfUiMessage must NOT ConfUiSecure{Selection,Touch}Message types
46  */
WrapWithSecureFlag(const ConfUiMessage & base_msg,const bool secure)47 static std::unique_ptr<ConfUiMessage> WrapWithSecureFlag(
48     const ConfUiMessage& base_msg, const bool secure) {
49   switch (base_msg.GetType()) {
50     case ConfUiCmd::kUserInputEvent: {
51       const ConfUiUserSelectionMessage& as_selection =
52           static_cast<const ConfUiUserSelectionMessage&>(base_msg);
53       return ToSecureSelectionMessage(as_selection, secure);
54     }
55     case ConfUiCmd::kUserTouchEvent: {
56       const ConfUiUserTouchMessage& as_touch =
57           static_cast<const ConfUiUserTouchMessage&>(base_msg);
58       return ToSecureTouchMessage(as_touch, secure);
59     }
60     default:
61       return nullptr;
62   }
63 }
64 
Get(HostModeCtrl & host_mode_ctrl,cuttlefish::ScreenConnectorFrameRenderer & screen_connector)65 HostServer& HostServer::Get(
66     HostModeCtrl& host_mode_ctrl,
67     cuttlefish::ScreenConnectorFrameRenderer& screen_connector) {
68   static HostServer host_server{host_mode_ctrl, screen_connector};
69   return host_server;
70 }
71 
HostServer(cuttlefish::HostModeCtrl & host_mode_ctrl,cuttlefish::ScreenConnectorFrameRenderer & screen_connector)72 HostServer::HostServer(
73     cuttlefish::HostModeCtrl& host_mode_ctrl,
74     cuttlefish::ScreenConnectorFrameRenderer& screen_connector)
75     : display_num_(0),
76       host_mode_ctrl_(host_mode_ctrl),
77       screen_connector_{screen_connector},
78       hal_vsock_port_(HalHostVsockPort()) {
79   ConfUiLog(DEBUG) << "Confirmation UI Host session is listening on: "
80                    << hal_vsock_port_;
81   const size_t max_elements = 20;
82   auto ignore_new =
__anon441b9e010102(ThreadSafeQueue<std::unique_ptr<ConfUiMessage>>::QueueImpl*) 83       [](ThreadSafeQueue<std::unique_ptr<ConfUiMessage>>::QueueImpl*) {
84         // no op, so the queue is still full, and the new item will be discarded
85         return;
86       };
87   hal_cmd_q_id_ = input_multiplexer_.RegisterQueue(
88       HostServer::Multiplexer::CreateQueue(max_elements, ignore_new));
89   user_input_evt_q_id_ = input_multiplexer_.RegisterQueue(
90       HostServer::Multiplexer::CreateQueue(max_elements, ignore_new));
91 }
92 
Start()93 void HostServer::Start() {
94   guest_hal_socket_ =
95       cuttlefish::SharedFD::VsockServer(hal_vsock_port_, SOCK_STREAM);
96   if (!guest_hal_socket_->IsOpen()) {
97     ConfUiLog(FATAL) << "Confirmation UI host service mandates a server socket"
98                      << "to which the guest HAL to connect.";
99     return;
100   }
101   auto hal_cmd_fetching = [this]() { this->HalCmdFetcherLoop(); };
102   auto main = [this]() { this->MainLoop(); };
103   hal_input_fetcher_thread_ =
104       thread::RunThread("HalInputLoop", hal_cmd_fetching);
105   main_loop_thread_ = thread::RunThread("MainLoop", main);
106   ConfUiLog(DEBUG) << "configured internal vsock based input.";
107   return;
108 }
109 
HalCmdFetcherLoop()110 void HostServer::HalCmdFetcherLoop() {
111   while (true) {
112     if (!hal_cli_socket_->IsOpen()) {
113       ConfUiLog(DEBUG) << "client is disconnected";
114       std::unique_lock<std::mutex> lk(socket_flag_mtx_);
115       hal_cli_socket_ = EstablishHalConnection();
116       is_socket_ok_ = true;
117       continue;
118     }
119     auto msg = RecvConfUiMsg(hal_cli_socket_);
120     if (!msg) {
121       ConfUiLog(ERROR) << "Error in RecvConfUiMsg from HAL";
122       hal_cli_socket_->Close();
123       is_socket_ok_ = false;
124       continue;
125     }
126     /*
127      * In case of Vts test, the msg could be a user input. For now, we do not
128      * enforce the input grace period for Vts. However, if ever we do, here is
129      * where the time point check should happen. Once it is enqueued, it is not
130      * always guaranteed to be picked up reasonably soon.
131      */
132     constexpr bool is_secure = false;
133     auto to_override_if_user_input = WrapWithSecureFlag(*msg, is_secure);
134     if (to_override_if_user_input) {
135       msg = std::move(to_override_if_user_input);
136     }
137     input_multiplexer_.Push(hal_cmd_q_id_, std::move(msg));
138   }
139 }
140 
SendUserSelection(std::unique_ptr<ConfUiMessage> & input)141 void HostServer::SendUserSelection(std::unique_ptr<ConfUiMessage>& input) {
142   if (!curr_session_ ||
143       curr_session_->GetState() != MainLoopState::kInSession ||
144       !curr_session_->IsReadyForUserInput()) {
145     // ignore
146     return;
147   }
148   constexpr bool is_secure = true;
149   auto secure_input = WrapWithSecureFlag(*input, is_secure);
150   input_multiplexer_.Push(user_input_evt_q_id_, std::move(secure_input));
151 }
152 
TouchEvent(const int x,const int y,const bool is_down)153 void HostServer::TouchEvent(const int x, const int y, const bool is_down) {
154   if (!is_down || !curr_session_) {
155     return;
156   }
157   std::unique_ptr<ConfUiMessage> input =
158       std::make_unique<ConfUiUserTouchMessage>(GetCurrentSessionId(), x, y);
159   constexpr bool is_secure = true;
160   auto secure_input = WrapWithSecureFlag(*input, is_secure);
161   SendUserSelection(secure_input);
162 }
163 
UserAbortEvent()164 void HostServer::UserAbortEvent() {
165   if (!curr_session_) {
166     return;
167   }
168   std::unique_ptr<ConfUiMessage> input =
169       std::make_unique<ConfUiUserSelectionMessage>(GetCurrentSessionId(),
170                                                    UserResponse::kUserAbort);
171   constexpr bool is_secure = true;
172   auto secure_input = WrapWithSecureFlag(*input, is_secure);
173   SendUserSelection(secure_input);
174 }
175 
IsConfUiActive()176 bool HostServer::IsConfUiActive() {
177   if (!curr_session_) {
178     return false;
179   }
180   return curr_session_->IsConfUiActive();
181 }
182 
EstablishHalConnection()183 SharedFD HostServer::EstablishHalConnection() {
184   using namespace std::chrono_literals;
185   while (true) {
186     ConfUiLog(VERBOSE) << "Waiting hal accepting";
187     auto new_cli = SharedFD::Accept(*guest_hal_socket_);
188     ConfUiLog(VERBOSE) << "hal client accepted";
189     if (new_cli->IsOpen()) {
190       return new_cli;
191     }
192     std::this_thread::sleep_for(500ms);
193   }
194 }
195 
196 // read the comments in the header file
MainLoop()197 [[noreturn]] void HostServer::MainLoop() {
198   while (true) {
199     // this gets one input from either queue:
200     // from HAL or from all webrtc clients
201     // if no input, sleep until there is
202     auto input_ptr = input_multiplexer_.Pop();
203     auto& input = *input_ptr;
204     const auto session_id = input.GetSessionId();
205     const auto cmd = input.GetType();
206     const std::string cmd_str(ToString(cmd));
207 
208     // take input for the Finite States Machine below
209     std::string src = input.IsUserInput() ? "input" : "hal";
210     ConfUiLog(VERBOSE) << "In Session " << GetCurrentSessionId() << ", "
211                        << "in state " << GetCurrentState() << ", "
212                        << "received input from " << src << " cmd =" << cmd_str
213                        << " going to session " << session_id;
214 
215     if (!curr_session_) {
216       if (cmd != ConfUiCmd::kStart) {
217         ConfUiLog(VERBOSE) << ToString(cmd) << " to " << session_id
218                            << " is ignored as there is no session to receive";
219         continue;
220       }
221       // the session is created as kInit
222       curr_session_ = CreateSession(input.GetSessionId());
223     }
224     if (cmd == ConfUiCmd::kUserTouchEvent) {
225       ConfUiSecureUserTouchMessage& touch_event =
226           static_cast<ConfUiSecureUserTouchMessage&>(input);
227       auto [x, y] = touch_event.GetLocation();
228       const bool is_confirm = curr_session_->IsConfirm(x, y);
229       const bool is_cancel = curr_session_->IsCancel(x, y);
230       if (!is_confirm && !is_cancel) {
231         // ignore, take the next input
232         continue;
233       }
234       decltype(input_ptr) tmp_input_ptr =
235           std::make_unique<ConfUiUserSelectionMessage>(
236               GetCurrentSessionId(),
237               (is_confirm ? UserResponse::kConfirm : UserResponse::kCancel));
238       input_ptr = WrapWithSecureFlag(*tmp_input_ptr, touch_event.IsSecure());
239     }
240     Transition(input_ptr);
241 
242     // finalize
243     if (curr_session_ &&
244         curr_session_->GetState() == MainLoopState::kAwaitCleanup) {
245       curr_session_->CleanUp();
246       curr_session_ = nullptr;
247     }
248   }  // end of the infinite while loop
249 }
250 
CreateSession(const std::string & name)251 std::shared_ptr<Session> HostServer::CreateSession(const std::string& name) {
252   return std::make_shared<Session>(name, display_num_, host_mode_ctrl_,
253                                    screen_connector_);
254 }
255 
IsUserAbort(ConfUiMessage & msg)256 static bool IsUserAbort(ConfUiMessage& msg) {
257   if (msg.GetType() != ConfUiCmd::kUserInputEvent) {
258     return false;
259   }
260   ConfUiUserSelectionMessage& selection =
261       static_cast<ConfUiUserSelectionMessage&>(msg);
262   return (selection.GetResponse() == UserResponse::kUserAbort);
263 }
264 
Transition(std::unique_ptr<ConfUiMessage> & input_ptr)265 void HostServer::Transition(std::unique_ptr<ConfUiMessage>& input_ptr) {
266   auto& input = *input_ptr;
267   const auto session_id = input.GetSessionId();
268   const auto cmd = input.GetType();
269   const std::string cmd_str(ToString(cmd));
270   FsmInput fsm_input = ToFsmInput(input);
271   ConfUiLog(VERBOSE) << "Handling " << ToString(cmd);
272   if (IsUserAbort(input)) {
273     curr_session_->UserAbort(hal_cli_socket_);
274     return;
275   }
276 
277   if (cmd == ConfUiCmd::kAbort) {
278     curr_session_->Abort();
279     return;
280   }
281   curr_session_->Transition(hal_cli_socket_, fsm_input, input);
282 }
283 
284 }  // end of namespace confui
285 }  // end of namespace cuttlefish
286