blob: be5caeb57ad248fc58059a82b6b790b4236e2e2a [file] [log] [blame]
/*
* 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.
*/
#pragma once
#include <atomic>
#include <cstdint>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
#include <android-base/logging.h>
#include <teeui/utils.h>
#include "common/libs/concurrency/multiplexer.h"
#include "common/libs/concurrency/semaphore.h"
#include "common/libs/confui/confui.h"
#include "common/libs/fs/shared_fd.h"
#include "host/commands/kernel_log_monitor/utils.h"
#include "host/libs/config/logging.h"
#include "host/libs/confui/host_mode_ctrl.h"
#include "host/libs/confui/host_renderer.h"
#include "host/libs/confui/host_virtual_input.h"
#include "host/libs/confui/server_common.h"
#include "host/libs/confui/session.h"
#include "host/libs/screen_connector/screen_connector.h"
namespace cuttlefish {
namespace confui {
class HostServer : public HostVirtualInput {
public:
static HostServer& Get(
HostModeCtrl& host_mode_ctrl,
cuttlefish::ScreenConnectorFrameRenderer& screen_connector);
void Start(); // start this server itself
virtual ~HostServer() = default;
// implement input interfaces. called by webRTC & vnc
void PressConfirmButton(const bool is_down) override;
void PressCancelButton(const bool is_down) override;
bool IsConfUiActive() override;
private:
explicit HostServer(
cuttlefish::HostModeCtrl& host_mode_ctrl,
cuttlefish::ScreenConnectorFrameRenderer& screen_connector);
HostServer() = delete;
/**
* basic prompt flow:
* (1) Without preemption
* send "kStart" with confirmation message
* wait kCliAck from the host service with the echoed command
* wait the confirmation/cancellation (or perhaps reset?)
* send kStop
* wait kCliAck from the host service with the echoed command
*
* (2) With preemption (e.g.)
* send "kStart" with confirmation message
* wait kCliAck from the host service with the echoed command
* wait the confirmation/cancellation (or perhaps reset?)
* send kSuspend // when HAL is preempted
* send kRestore // when HAL resumes
* send kStop
*
* From the host end, it is a close-to-Mealy FSM.
* There are four states S = {init, session, wait_ack, suspended}
*
* 'session' means in a confirmation session. 'wait_ack' means
* server sends the confirmation and waiting "stop" command from HAL
* 'suspended' means the HAL service is preemptied. So, the host
* should render the Android guest frames but keep the confirmation
* UI session and frame
*
* The inputs are I = {u, g}. 'u' is the user input from vnc/webRTC
* clients. Note that the host service serialized the concurrent user
* inputs from multiple clients. 'g' is the command from the HAL service
*
* The transition rules:
* (S, I) --> (S, O) where O is the output
*
* init, g(start) --> session, set Conf UI mode, render a frame
* session, u(cancel/confirm) --> waitstop, send the result to HAL
* session, g(suspend) --> suspend, create a saved session
* session, g(abort) --> init, clear saved frame
* waitstop, g(stop) --> init, clear saved frame
* waitstop, g(suspend) --> suspend, no need to save the session
* waitstop, g(abort) --> init, clear saved frame
* suspend, g(restore) --> return to the saved state, restore if there's a
* saved session
* suspend, g(abort) --> init, clear saved frame
*
* For now, we did not yet implement suspend or abort.
*
*/
[[noreturn]] void MainLoop();
void HalCmdFetcherLoop();
SharedFD EstablishHalConnection();
// failed to start dialog, etc
// basically, will reset the session, so start from the beginning in the same
// session
void ResetOnCommandFailure();
// note: the picked session will be removed from session_map_
std::unique_ptr<Session> GetSession(const std::string& session_id) {
if (session_map_.find(session_id) == session_map_.end()) {
return nullptr;
}
std::unique_ptr<Session> temp = std::move(session_map_[session_id]);
session_map_.erase(session_id);
return temp;
}
std::string GetCurrentSessionId() {
if (curr_session_) {
return curr_session_->GetId();
}
return SESSION_ANY;
}
std::string GetCurrentState() {
if (!curr_session_) {
return {"kInvalid"};
}
return ToString(curr_session_->GetState());
}
std::unique_ptr<Session> ComputeCurrentSession(const std::string& session_id);
bool SendUserSelection(UserResponse::type selection);
const std::uint32_t display_num_;
HostModeCtrl& host_mode_ctrl_;
ScreenConnectorFrameRenderer& screen_connector_;
// this member creates a raw frame
ConfUiRenderer renderer_;
std::string input_socket_path_;
std::string hal_socket_path_;
// session id to Session object map, for those that are suspended
std::unordered_map<std::string, std::unique_ptr<Session>> session_map_;
// curr_session_ doesn't belong to session_map_
std::unique_ptr<Session> curr_session_;
SharedFD guest_hal_socket_;
// ACCEPTED fd on guest_hal_socket_
SharedFD hal_cli_socket_;
std::mutex input_socket_mtx_;
/*
* Multiplexer has N queues. When pop(), it is going to sleep until
* there's at least one item in at least one queue. The lower the Q
* index is, the higher the priority is.
*
* For HostServer, we have a queue for the user input events, and
* another for hal cmd/msg queues
*/
Multiplexer<ConfUiMessage> input_multiplexer_;
int hal_cmd_q_id_; // Q id in input_multiplexer_
int user_input_evt_q_id_; // Q id in input_multiplexer_
std::thread main_loop_thread_;
std::thread hal_input_fetcher_thread_;
};
} // end of namespace confui
} // end of namespace cuttlefish