You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
260 lines
8.6 KiB
260 lines
8.6 KiB
/*
|
|
* Copyright (C) 2021 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "host/libs/confui/host_server.h"
|
|
|
|
#include <chrono>
|
|
#include <functional>
|
|
#include <optional>
|
|
#include <tuple>
|
|
|
|
#include "common/libs/confui/confui.h"
|
|
#include "host/libs/config/cuttlefish_config.h"
|
|
#include "host/libs/confui/host_utils.h"
|
|
|
|
namespace cuttlefish {
|
|
namespace confui {
|
|
static auto CuttlefishConfigDefaultInstance() {
|
|
auto config = cuttlefish::CuttlefishConfig::Get();
|
|
CHECK(config) << "Config must not be null";
|
|
return config->ForDefaultInstance();
|
|
}
|
|
|
|
static std::string HalGuestSocketPath() {
|
|
return CuttlefishConfigDefaultInstance().confui_hal_guest_socket_path();
|
|
}
|
|
|
|
HostServer& HostServer::Get(
|
|
HostModeCtrl& host_mode_ctrl,
|
|
cuttlefish::ScreenConnectorFrameRenderer& screen_connector) {
|
|
static HostServer host_server{host_mode_ctrl, screen_connector};
|
|
return host_server;
|
|
}
|
|
|
|
HostServer::HostServer(
|
|
cuttlefish::HostModeCtrl& host_mode_ctrl,
|
|
cuttlefish::ScreenConnectorFrameRenderer& screen_connector)
|
|
: display_num_(0),
|
|
host_mode_ctrl_(host_mode_ctrl),
|
|
screen_connector_{screen_connector},
|
|
renderer_(display_num_),
|
|
hal_socket_path_(HalGuestSocketPath()),
|
|
input_multiplexer_{/* max n_elems */ 20, /* n_Qs */ 2} {
|
|
hal_cmd_q_id_ = input_multiplexer_.GetNewQueueId(); // return 0
|
|
user_input_evt_q_id_ = input_multiplexer_.GetNewQueueId(); // return 1
|
|
}
|
|
|
|
void HostServer::Start() {
|
|
guest_hal_socket_ = cuttlefish::SharedFD::SocketLocalServer(
|
|
hal_socket_path_, false, SOCK_STREAM, 0666);
|
|
if (!guest_hal_socket_->IsOpen()) {
|
|
ConfUiLog(FATAL) << "Confirmation UI host service mandates a server socket"
|
|
<< "to which the guest HAL to connect.";
|
|
return;
|
|
}
|
|
auto hal_cmd_fetching = [this]() { this->HalCmdFetcherLoop(); };
|
|
auto main = [this]() { this->MainLoop(); };
|
|
hal_input_fetcher_thread_ =
|
|
thread::RunThread("HalInputLoop", hal_cmd_fetching);
|
|
main_loop_thread_ = thread::RunThread("MainLoop", main);
|
|
ConfUiLog(DEBUG) << "configured internal socket based input.";
|
|
return;
|
|
}
|
|
|
|
void HostServer::HalCmdFetcherLoop() {
|
|
hal_cli_socket_ = EstablishHalConnection();
|
|
if (!hal_cli_socket_->IsOpen()) {
|
|
ConfUiLog(FATAL)
|
|
<< "Confirmation UI host service mandates connection with HAL.";
|
|
return;
|
|
}
|
|
while (true) {
|
|
auto opted_msg = RecvConfUiMsg(hal_cli_socket_);
|
|
if (!opted_msg) {
|
|
ConfUiLog(ERROR) << "Error in RecvConfUiMsg from HAL";
|
|
continue;
|
|
}
|
|
auto input = std::move(opted_msg.value());
|
|
input_multiplexer_.Push(hal_cmd_q_id_, std::move(input));
|
|
}
|
|
}
|
|
|
|
bool HostServer::SendUserSelection(UserResponse::type selection) {
|
|
if (!curr_session_) {
|
|
ConfUiLog(FATAL) << "Current session must not be null";
|
|
return false;
|
|
}
|
|
if (curr_session_->GetState() != MainLoopState::kInSession) {
|
|
// ignore
|
|
return true;
|
|
}
|
|
|
|
std::lock_guard<std::mutex> lock(input_socket_mtx_);
|
|
if (selection != UserResponse::kConfirm &&
|
|
selection != UserResponse::kCancel) {
|
|
ConfUiLog(FATAL) << selection << " must be either" << UserResponse::kConfirm
|
|
<< "or" << UserResponse::kCancel;
|
|
return false; // not reaching here
|
|
}
|
|
|
|
ConfUiMessage input{GetCurrentSessionId(),
|
|
ToString(ConfUiCmd::kUserInputEvent), selection};
|
|
|
|
input_multiplexer_.Push(user_input_evt_q_id_, std::move(input));
|
|
return true;
|
|
}
|
|
|
|
void HostServer::PressConfirmButton(const bool is_down) {
|
|
if (!is_down) {
|
|
return;
|
|
}
|
|
// shared by N vnc/webRTC clients
|
|
SendUserSelection(UserResponse::kConfirm);
|
|
}
|
|
|
|
void HostServer::PressCancelButton(const bool is_down) {
|
|
if (!is_down) {
|
|
return;
|
|
}
|
|
// shared by N vnc/webRTC clients
|
|
SendUserSelection(UserResponse::kCancel);
|
|
}
|
|
|
|
bool HostServer::IsConfUiActive() {
|
|
if (!curr_session_) {
|
|
return false;
|
|
}
|
|
return curr_session_->IsConfUiActive();
|
|
}
|
|
|
|
SharedFD HostServer::EstablishHalConnection() {
|
|
ConfUiLog(DEBUG) << "Waiting hal accepting";
|
|
auto new_cli = SharedFD::Accept(*guest_hal_socket_);
|
|
ConfUiLog(DEBUG) << "hal client accepted";
|
|
return new_cli;
|
|
}
|
|
|
|
std::unique_ptr<Session> HostServer::ComputeCurrentSession(
|
|
const std::string& session_id) {
|
|
if (curr_session_ && (GetCurrentSessionId() != session_id)) {
|
|
ConfUiLog(FATAL) << curr_session_->GetId() << " is active and in the"
|
|
<< GetCurrentState() << "but HAL sends command to"
|
|
<< session_id;
|
|
}
|
|
if (curr_session_) {
|
|
return std::move(curr_session_);
|
|
}
|
|
|
|
// pick up a new session, or create one
|
|
auto result = GetSession(session_id);
|
|
if (result) {
|
|
return std::move(result);
|
|
}
|
|
|
|
auto raw_ptr = new Session(session_id, display_num_, renderer_,
|
|
host_mode_ctrl_, screen_connector_);
|
|
result = std::unique_ptr<Session>(raw_ptr);
|
|
// note that the new session is directly going to curr_session_
|
|
// when it is suspended, it will be moved to session_map_
|
|
return std::move(result);
|
|
}
|
|
|
|
// read the comments in the header file
|
|
[[noreturn]] void HostServer::MainLoop() {
|
|
while (true) {
|
|
// this gets one input from either queue:
|
|
// from HAL or from all webrtc/vnc clients
|
|
// if no input, sleep until there is
|
|
const auto input = input_multiplexer_.Pop();
|
|
const auto& [session_id, cmd_str, additional_info] = input;
|
|
|
|
// take input for the Finite States Machine below
|
|
const ConfUiCmd cmd = ToCmd(cmd_str);
|
|
const bool is_user_input = (cmd == ConfUiCmd::kUserInputEvent);
|
|
std::string src = is_user_input ? "input" : "hal";
|
|
|
|
ConfUiLog(DEBUG) << "In Session" << GetCurrentSessionId() << ","
|
|
<< "in state" << GetCurrentState() << ","
|
|
<< "received input from" << src << "cmd =" << cmd_str
|
|
<< "and additional_info =" << additional_info
|
|
<< "going to session" << session_id;
|
|
|
|
FsmInput fsm_input = ToFsmInput(input);
|
|
|
|
if (is_user_input && !curr_session_) {
|
|
// discard the input, there's no session to take it yet
|
|
// actually, no confirmation UI screen is available
|
|
ConfUiLog(DEBUG) << "Took user input but no active session is available.";
|
|
continue;
|
|
}
|
|
|
|
/**
|
|
* if the curr_session_ is null, create one
|
|
* if the curr_session_ is not null but the session id doesn't match,
|
|
* something is wrong. Confirmation UI doesn't allow preemption by
|
|
* another confirmation UI session back to back. When it's preempted,
|
|
* HAL must send "kSuspend"
|
|
*
|
|
*/
|
|
curr_session_ = ComputeCurrentSession(session_id);
|
|
ConfUiLog(DEBUG) << "Host service picked up "
|
|
<< (curr_session_ ? curr_session_->GetId()
|
|
: "null session");
|
|
ConfUiLog(DEBUG) << "The state of current session is "
|
|
<< (curr_session_ ? ToString(curr_session_->GetState())
|
|
: "null session");
|
|
|
|
if (is_user_input) {
|
|
curr_session_->Transition(is_user_input, hal_cli_socket_, fsm_input,
|
|
additional_info);
|
|
} else {
|
|
ConfUiCmd cmd = ToCmd(cmd_str);
|
|
switch (cmd) {
|
|
case ConfUiCmd::kSuspend:
|
|
curr_session_->Suspend(hal_cli_socket_);
|
|
break;
|
|
case ConfUiCmd::kRestore:
|
|
curr_session_->Restore(hal_cli_socket_);
|
|
break;
|
|
case ConfUiCmd::kAbort:
|
|
curr_session_->Abort(hal_cli_socket_);
|
|
break;
|
|
default:
|
|
curr_session_->Transition(is_user_input, hal_cli_socket_, fsm_input,
|
|
additional_info);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// check the session if it is inactive (e.g. init, suspended)
|
|
// and if it is done (transitioned to init from any other state)
|
|
if (curr_session_->IsSuspended()) {
|
|
session_map_[GetCurrentSessionId()] = std::move(curr_session_);
|
|
curr_session_ = nullptr;
|
|
continue;
|
|
}
|
|
|
|
if (curr_session_->GetState() == MainLoopState::kAwaitCleanup) {
|
|
curr_session_->CleanUp();
|
|
curr_session_ = nullptr;
|
|
}
|
|
// otherwise, continue with keeping the curr_session_
|
|
} // end of the infinite while loop
|
|
}
|
|
|
|
} // end of namespace confui
|
|
} // end of namespace cuttlefish
|