307 lines
10 KiB
C++
307 lines
10 KiB
C++
/*
|
|
* 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/session.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "host/libs/confui/secure_input.h"
|
|
|
|
namespace cuttlefish {
|
|
namespace confui {
|
|
|
|
Session::Session(const std::string& session_name,
|
|
const std::uint32_t display_num, HostModeCtrl& host_mode_ctrl,
|
|
ScreenConnectorFrameRenderer& screen_connector,
|
|
const std::string& locale)
|
|
: session_id_{session_name},
|
|
display_num_{display_num},
|
|
host_mode_ctrl_{host_mode_ctrl},
|
|
screen_connector_{screen_connector},
|
|
locale_{locale},
|
|
state_{MainLoopState::kInit},
|
|
saved_state_{MainLoopState::kInit} {}
|
|
|
|
/** return grace period + alpha
|
|
*
|
|
* grace period is the gap between user seeing the dialog
|
|
* and the UI starts to take the user inputs
|
|
* Grace period should be at least 1s.
|
|
* Session requests the Renderer to render the dialog,
|
|
* but it might not be immediate. So, add alpha to 1s
|
|
*/
|
|
static const std::chrono::milliseconds GetGracePeriod() {
|
|
using std::literals::chrono_literals::operator""ms;
|
|
return 1000ms + 100ms;
|
|
}
|
|
|
|
bool Session::IsReadyForUserInput() const {
|
|
using std::literals::chrono_literals::operator""ms;
|
|
if (!start_time_) {
|
|
return false;
|
|
}
|
|
const auto right_now = Clock::now();
|
|
return (right_now - *start_time_) >= GetGracePeriod();
|
|
}
|
|
|
|
bool Session::IsConfUiActive() const {
|
|
if (state_ == MainLoopState::kInSession ||
|
|
state_ == MainLoopState::kWaitStop) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename C, typename T>
|
|
static bool Contains(const C& c, T&& item) {
|
|
auto itr = std::find(c.begin(), c.end(), std::forward<T>(item));
|
|
return itr != c.end();
|
|
}
|
|
|
|
bool Session::IsInverted() const {
|
|
return Contains(ui_options_, teeui::UIOption::AccessibilityInverted);
|
|
}
|
|
|
|
bool Session::IsMagnified() const {
|
|
return Contains(ui_options_, teeui::UIOption::AccessibilityMagnified);
|
|
}
|
|
|
|
bool Session::RenderDialog() {
|
|
renderer_ = ConfUiRenderer::GenerateRenderer(
|
|
display_num_, prompt_text_, locale_, IsInverted(), IsMagnified());
|
|
if (!renderer_) {
|
|
return false;
|
|
}
|
|
auto teeui_frame = renderer_->RenderRawFrame();
|
|
if (!teeui_frame) {
|
|
return false;
|
|
}
|
|
ConfUiLog(VERBOSE) << "actually trying to render the frame"
|
|
<< thread::GetName();
|
|
auto frame_width = teeui_frame->Width();
|
|
auto frame_height = teeui_frame->Height();
|
|
auto frame_stride_bytes = teeui_frame->ScreenStrideBytes();
|
|
auto frame_bytes = reinterpret_cast<std::uint8_t*>(teeui_frame->data());
|
|
return screen_connector_.RenderConfirmationUi(
|
|
display_num_, frame_width, frame_height, frame_stride_bytes, frame_bytes);
|
|
}
|
|
|
|
MainLoopState Session::Transition(SharedFD& hal_cli, const FsmInput fsm_input,
|
|
const ConfUiMessage& conf_ui_message) {
|
|
bool should_keep_running = false;
|
|
bool already_terminated = false;
|
|
switch (state_) {
|
|
case MainLoopState::kInit: {
|
|
should_keep_running = HandleInit(hal_cli, fsm_input, conf_ui_message);
|
|
} break;
|
|
case MainLoopState::kInSession: {
|
|
should_keep_running =
|
|
HandleInSession(hal_cli, fsm_input, conf_ui_message);
|
|
} break;
|
|
case MainLoopState::kWaitStop: {
|
|
if (IsUserInput(fsm_input)) {
|
|
ConfUiLog(VERBOSE) << "User input ignored " << ToString(fsm_input)
|
|
<< " : " << ToString(conf_ui_message)
|
|
<< " at the state " << ToString(state_);
|
|
}
|
|
should_keep_running = HandleWaitStop(hal_cli, fsm_input);
|
|
} break;
|
|
case MainLoopState::kTerminated: {
|
|
already_terminated = true;
|
|
} break;
|
|
default:
|
|
ConfUiLog(FATAL) << "Must not be in the state of " << ToString(state_);
|
|
break;
|
|
}
|
|
if (!should_keep_running && !already_terminated) {
|
|
ScheduleToTerminate();
|
|
}
|
|
return state_;
|
|
};
|
|
|
|
void Session::CleanUp() {
|
|
if (state_ != MainLoopState::kAwaitCleanup) {
|
|
ConfUiLog(FATAL) << "Clean up a session only when in kAwaitCleanup";
|
|
}
|
|
state_ = MainLoopState::kTerminated;
|
|
// common action done when the state is back to init state
|
|
host_mode_ctrl_.SetMode(HostModeCtrl::ModeType::kAndroidMode);
|
|
}
|
|
|
|
void Session::ScheduleToTerminate() {
|
|
state_ = MainLoopState::kAwaitCleanup;
|
|
saved_state_ = MainLoopState::kInvalid;
|
|
}
|
|
|
|
bool Session::ReportErrorToHal(SharedFD hal_cli, const std::string& msg) {
|
|
ScheduleToTerminate();
|
|
if (!SendAck(hal_cli, session_id_, false, msg)) {
|
|
ConfUiLog(ERROR) << "I/O error in sending ack to report rendering failure";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Session::Abort() {
|
|
ConfUiLog(VERBOSE) << "Abort is called";
|
|
ScheduleToTerminate();
|
|
return;
|
|
}
|
|
|
|
void Session::UserAbort(SharedFD hal_cli) {
|
|
ConfUiLog(VERBOSE) << "it is a user abort input.";
|
|
SendAbortCmd(hal_cli, GetId());
|
|
Abort();
|
|
ScheduleToTerminate();
|
|
}
|
|
|
|
bool Session::HandleInit(SharedFD hal_cli, const FsmInput fsm_input,
|
|
const ConfUiMessage& conf_ui_message) {
|
|
if (IsUserInput(fsm_input)) {
|
|
// ignore user input
|
|
state_ = MainLoopState::kInit;
|
|
return true;
|
|
}
|
|
|
|
ConfUiLog(VERBOSE) << ToString(fsm_input) << "is handled in HandleInit";
|
|
if (fsm_input != FsmInput::kHalStart) {
|
|
ConfUiLog(ERROR) << "invalid cmd for Init State:" << ToString(fsm_input);
|
|
// ReportErrorToHal returns true if error report was successful
|
|
// However, anyway we abort this session on the host
|
|
ReportErrorToHal(hal_cli, HostError::kSystemError);
|
|
return false;
|
|
}
|
|
|
|
// Start Session
|
|
ConfUiLog(VERBOSE) << "Sending ack to hal_cli: "
|
|
<< Enum2Base(ConfUiCmd::kCliAck);
|
|
host_mode_ctrl_.SetMode(HostModeCtrl::ModeType::kConfUI_Mode);
|
|
|
|
auto start_cmd_msg = static_cast<const ConfUiStartMessage&>(conf_ui_message);
|
|
prompt_text_ = start_cmd_msg.GetPromptText();
|
|
locale_ = start_cmd_msg.GetLocale();
|
|
extra_data_ = start_cmd_msg.GetExtraData();
|
|
ui_options_ = start_cmd_msg.GetUiOpts();
|
|
|
|
// cbor_ can be correctly created after the session received kStart cmd
|
|
// at runtime
|
|
cbor_ = std::make_unique<Cbor>(prompt_text_, extra_data_);
|
|
if (cbor_->IsMessageTooLong()) {
|
|
ConfUiLog(ERROR) << "The prompt text and extra_data are too long to be "
|
|
<< "properly encoded.";
|
|
ReportErrorToHal(hal_cli, HostError::kMessageTooLongError);
|
|
return false;
|
|
}
|
|
if (cbor_->IsMalformedUtf8()) {
|
|
ConfUiLog(ERROR) << "The prompt text appears to have incorrect UTF8 format";
|
|
ReportErrorToHal(hal_cli, HostError::kIncorrectUTF8);
|
|
return false;
|
|
}
|
|
if (!cbor_->IsOk()) {
|
|
ConfUiLog(ERROR) << "Unknown Error in cbor implementation";
|
|
ReportErrorToHal(hal_cli, HostError::kSystemError);
|
|
return false;
|
|
}
|
|
|
|
if (!RenderDialog()) {
|
|
// the confirmation UI is driven by a user app, not running from the start
|
|
// automatically so that means webRTC should have been set up
|
|
ConfUiLog(ERROR) << "Dialog is not rendered. However, it should."
|
|
<< "No webRTC can't initiate any confirmation UI.";
|
|
ReportErrorToHal(hal_cli, HostError::kUIError);
|
|
return false;
|
|
}
|
|
start_time_ = std::make_unique<TimePoint>(std::move(Clock::now()));
|
|
if (!SendAck(hal_cli, session_id_, true, "started")) {
|
|
ConfUiLog(ERROR) << "Ack to kStart failed in I/O";
|
|
return false;
|
|
}
|
|
state_ = MainLoopState::kInSession;
|
|
return true;
|
|
}
|
|
|
|
bool Session::HandleInSession(SharedFD hal_cli, const FsmInput fsm_input,
|
|
const ConfUiMessage& conf_ui_msg) {
|
|
auto invalid_input_handler = [&, this]() {
|
|
ReportErrorToHal(hal_cli, HostError::kSystemError);
|
|
ConfUiLog(ERROR) << "cmd " << ToString(fsm_input)
|
|
<< " should not be handled in HandleInSession";
|
|
};
|
|
|
|
if (!IsUserInput(fsm_input)) {
|
|
invalid_input_handler();
|
|
return false;
|
|
}
|
|
|
|
const auto& user_input_msg =
|
|
static_cast<const ConfUiSecureUserSelectionMessage&>(conf_ui_msg);
|
|
const auto response = user_input_msg.GetResponse();
|
|
if (response == UserResponse::kUnknown ||
|
|
response == UserResponse::kUserAbort) {
|
|
invalid_input_handler();
|
|
return false;
|
|
}
|
|
const bool is_secure_input = user_input_msg.IsSecure();
|
|
|
|
ConfUiLog(VERBOSE) << "In HandleInSession, session " << session_id_
|
|
<< " is sending the user input " << ToString(fsm_input);
|
|
|
|
bool is_success = false;
|
|
if (response == UserResponse::kCancel) {
|
|
// no need to sign
|
|
is_success =
|
|
SendResponse(hal_cli, session_id_, UserResponse::kCancel,
|
|
std::vector<std::uint8_t>{}, std::vector<std::uint8_t>{});
|
|
} else {
|
|
message_ = std::move(cbor_->GetMessage());
|
|
auto message_opt = (is_secure_input ? Sign(message_) : TestSign(message_));
|
|
if (!message_opt) {
|
|
ReportErrorToHal(hal_cli, HostError::kSystemError);
|
|
return false;
|
|
}
|
|
signed_confirmation_ = message_opt.value();
|
|
is_success = SendResponse(hal_cli, session_id_, UserResponse::kConfirm,
|
|
signed_confirmation_, message_);
|
|
}
|
|
|
|
if (!is_success) {
|
|
ConfUiLog(ERROR) << "I/O error in sending user response to HAL";
|
|
return false;
|
|
}
|
|
state_ = MainLoopState::kWaitStop;
|
|
return true;
|
|
}
|
|
|
|
bool Session::HandleWaitStop(SharedFD hal_cli, const FsmInput fsm_input) {
|
|
if (IsUserInput(fsm_input)) {
|
|
// ignore user input
|
|
state_ = MainLoopState::kWaitStop;
|
|
return true;
|
|
}
|
|
if (fsm_input == FsmInput::kHalStop) {
|
|
ConfUiLog(VERBOSE) << "Handling Abort in kWaitStop.";
|
|
ScheduleToTerminate();
|
|
return true;
|
|
}
|
|
ReportErrorToHal(hal_cli, HostError::kSystemError);
|
|
ConfUiLog(FATAL) << "In WaitStop, received wrong HAL command "
|
|
<< ToString(fsm_input);
|
|
return false;
|
|
}
|
|
|
|
} // end of namespace confui
|
|
} // end of namespace cuttlefish
|