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 "host/libs/config/cuttlefish_config.h"
26 #include "host/libs/confui/host_utils.h"
27
28 namespace cuttlefish {
29 namespace confui {
CuttlefishConfigDefaultInstance()30 static auto CuttlefishConfigDefaultInstance() {
31 auto config = cuttlefish::CuttlefishConfig::Get();
32 CHECK(config) << "Config must not be null";
33 return config->ForDefaultInstance();
34 }
35
HalGuestSocketPath()36 static std::string HalGuestSocketPath() {
37 return CuttlefishConfigDefaultInstance().confui_hal_guest_socket_path();
38 }
39
Get(HostModeCtrl & host_mode_ctrl,cuttlefish::ScreenConnectorFrameRenderer & screen_connector)40 HostServer& HostServer::Get(
41 HostModeCtrl& host_mode_ctrl,
42 cuttlefish::ScreenConnectorFrameRenderer& screen_connector) {
43 static HostServer host_server{host_mode_ctrl, screen_connector};
44 return host_server;
45 }
46
HostServer(cuttlefish::HostModeCtrl & host_mode_ctrl,cuttlefish::ScreenConnectorFrameRenderer & screen_connector)47 HostServer::HostServer(
48 cuttlefish::HostModeCtrl& host_mode_ctrl,
49 cuttlefish::ScreenConnectorFrameRenderer& screen_connector)
50 : display_num_(0),
51 host_mode_ctrl_(host_mode_ctrl),
52 screen_connector_{screen_connector},
53 renderer_(display_num_),
54 hal_socket_path_(HalGuestSocketPath()),
55 input_multiplexer_{/* max n_elems */ 20, /* n_Qs */ 2} {
56 hal_cmd_q_id_ = input_multiplexer_.GetNewQueueId(); // return 0
57 user_input_evt_q_id_ = input_multiplexer_.GetNewQueueId(); // return 1
58 }
59
Start()60 void HostServer::Start() {
61 guest_hal_socket_ = cuttlefish::SharedFD::SocketLocalServer(
62 hal_socket_path_, false, SOCK_STREAM, 0666);
63 if (!guest_hal_socket_->IsOpen()) {
64 ConfUiLog(FATAL) << "Confirmation UI host service mandates a server socket"
65 << "to which the guest HAL to connect.";
66 return;
67 }
68 auto hal_cmd_fetching = [this]() { this->HalCmdFetcherLoop(); };
69 auto main = [this]() { this->MainLoop(); };
70 hal_input_fetcher_thread_ =
71 thread::RunThread("HalInputLoop", hal_cmd_fetching);
72 main_loop_thread_ = thread::RunThread("MainLoop", main);
73 ConfUiLog(DEBUG) << "configured internal socket based input.";
74 return;
75 }
76
HalCmdFetcherLoop()77 void HostServer::HalCmdFetcherLoop() {
78 hal_cli_socket_ = EstablishHalConnection();
79 if (!hal_cli_socket_->IsOpen()) {
80 ConfUiLog(FATAL)
81 << "Confirmation UI host service mandates connection with HAL.";
82 return;
83 }
84 while (true) {
85 auto opted_msg = RecvConfUiMsg(hal_cli_socket_);
86 if (!opted_msg) {
87 ConfUiLog(ERROR) << "Error in RecvConfUiMsg from HAL";
88 continue;
89 }
90 auto input = std::move(opted_msg.value());
91 input_multiplexer_.Push(hal_cmd_q_id_, std::move(input));
92 }
93 }
94
SendUserSelection(UserResponse::type selection)95 bool HostServer::SendUserSelection(UserResponse::type selection) {
96 if (!curr_session_) {
97 ConfUiLog(FATAL) << "Current session must not be null";
98 return false;
99 }
100 if (curr_session_->GetState() != MainLoopState::kInSession) {
101 // ignore
102 return true;
103 }
104
105 std::lock_guard<std::mutex> lock(input_socket_mtx_);
106 if (selection != UserResponse::kConfirm &&
107 selection != UserResponse::kCancel) {
108 ConfUiLog(FATAL) << selection << " must be either" << UserResponse::kConfirm
109 << "or" << UserResponse::kCancel;
110 return false; // not reaching here
111 }
112
113 ConfUiMessage input{GetCurrentSessionId(),
114 ToString(ConfUiCmd::kUserInputEvent), selection};
115
116 input_multiplexer_.Push(user_input_evt_q_id_, std::move(input));
117 return true;
118 }
119
PressConfirmButton(const bool is_down)120 void HostServer::PressConfirmButton(const bool is_down) {
121 if (!is_down) {
122 return;
123 }
124 // shared by N vnc/webRTC clients
125 SendUserSelection(UserResponse::kConfirm);
126 }
127
PressCancelButton(const bool is_down)128 void HostServer::PressCancelButton(const bool is_down) {
129 if (!is_down) {
130 return;
131 }
132 // shared by N vnc/webRTC clients
133 SendUserSelection(UserResponse::kCancel);
134 }
135
IsConfUiActive()136 bool HostServer::IsConfUiActive() {
137 if (!curr_session_) {
138 return false;
139 }
140 return curr_session_->IsConfUiActive();
141 }
142
EstablishHalConnection()143 SharedFD HostServer::EstablishHalConnection() {
144 ConfUiLog(DEBUG) << "Waiting hal accepting";
145 auto new_cli = SharedFD::Accept(*guest_hal_socket_);
146 ConfUiLog(DEBUG) << "hal client accepted";
147 return new_cli;
148 }
149
ComputeCurrentSession(const std::string & session_id)150 std::unique_ptr<Session> HostServer::ComputeCurrentSession(
151 const std::string& session_id) {
152 if (curr_session_ && (GetCurrentSessionId() != session_id)) {
153 ConfUiLog(FATAL) << curr_session_->GetId() << " is active and in the"
154 << GetCurrentState() << "but HAL sends command to"
155 << session_id;
156 }
157 if (curr_session_) {
158 return std::move(curr_session_);
159 }
160
161 // pick up a new session, or create one
162 auto result = GetSession(session_id);
163 if (result) {
164 return std::move(result);
165 }
166
167 auto raw_ptr = new Session(session_id, display_num_, renderer_,
168 host_mode_ctrl_, screen_connector_);
169 result = std::unique_ptr<Session>(raw_ptr);
170 // note that the new session is directly going to curr_session_
171 // when it is suspended, it will be moved to session_map_
172 return std::move(result);
173 }
174
175 // read the comments in the header file
MainLoop()176 [[noreturn]] void HostServer::MainLoop() {
177 while (true) {
178 // this gets one input from either queue:
179 // from HAL or from all webrtc/vnc clients
180 // if no input, sleep until there is
181 const auto input = input_multiplexer_.Pop();
182 const auto& [session_id, cmd_str, additional_info] = input;
183
184 // take input for the Finite States Machine below
185 const ConfUiCmd cmd = ToCmd(cmd_str);
186 const bool is_user_input = (cmd == ConfUiCmd::kUserInputEvent);
187 std::string src = is_user_input ? "input" : "hal";
188
189 ConfUiLog(DEBUG) << "In Session" << GetCurrentSessionId() << ","
190 << "in state" << GetCurrentState() << ","
191 << "received input from" << src << "cmd =" << cmd_str
192 << "and additional_info =" << additional_info
193 << "going to session" << session_id;
194
195 FsmInput fsm_input = ToFsmInput(input);
196
197 if (is_user_input && !curr_session_) {
198 // discard the input, there's no session to take it yet
199 // actually, no confirmation UI screen is available
200 ConfUiLog(DEBUG) << "Took user input but no active session is available.";
201 continue;
202 }
203
204 /**
205 * if the curr_session_ is null, create one
206 * if the curr_session_ is not null but the session id doesn't match,
207 * something is wrong. Confirmation UI doesn't allow preemption by
208 * another confirmation UI session back to back. When it's preempted,
209 * HAL must send "kSuspend"
210 *
211 */
212 curr_session_ = ComputeCurrentSession(session_id);
213 ConfUiLog(DEBUG) << "Host service picked up "
214 << (curr_session_ ? curr_session_->GetId()
215 : "null session");
216 ConfUiLog(DEBUG) << "The state of current session is "
217 << (curr_session_ ? ToString(curr_session_->GetState())
218 : "null session");
219
220 if (is_user_input) {
221 curr_session_->Transition(is_user_input, hal_cli_socket_, fsm_input,
222 additional_info);
223 } else {
224 ConfUiCmd cmd = ToCmd(cmd_str);
225 switch (cmd) {
226 case ConfUiCmd::kSuspend:
227 curr_session_->Suspend(hal_cli_socket_);
228 break;
229 case ConfUiCmd::kRestore:
230 curr_session_->Restore(hal_cli_socket_);
231 break;
232 case ConfUiCmd::kAbort:
233 curr_session_->Abort(hal_cli_socket_);
234 break;
235 default:
236 curr_session_->Transition(is_user_input, hal_cli_socket_, fsm_input,
237 additional_info);
238 break;
239 }
240 }
241
242 // check the session if it is inactive (e.g. init, suspended)
243 // and if it is done (transitioned to init from any other state)
244 if (curr_session_->IsSuspended()) {
245 session_map_[GetCurrentSessionId()] = std::move(curr_session_);
246 curr_session_ = nullptr;
247 continue;
248 }
249
250 if (curr_session_->GetState() == MainLoopState::kAwaitCleanup) {
251 curr_session_->CleanUp();
252 curr_session_ = nullptr;
253 }
254 // otherwise, continue with keeping the curr_session_
255 } // end of the infinite while loop
256 }
257
258 } // end of namespace confui
259 } // end of namespace cuttlefish
260