blob: 827bbd65ddd971140b100c3947ff74c00ef01f44 [file] [log] [blame] [edit]
/*
*
* Copyright 2019, 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 "TrustyConfirmationUI.h"
#include <android/binder_manager.h>
#include <cutils/properties.h>
namespace aidl::android::hardware::confirmationui {
using ::teeui::MsgString;
using ::teeui::MsgVector;
using TeeuiRc = ::teeui::ResponseCode;
namespace {
teeui::UIOption convertUIOption(UIOption uio) {
static_assert(uint32_t(UIOption::ACCESSIBILITY_INVERTED) ==
uint32_t(teeui::UIOption::AccessibilityInverted) &&
uint32_t(UIOption::ACCESSIBILITY_MAGNIFIED) ==
uint32_t(teeui::UIOption::AccessibilityMagnified),
"teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption "
"are out of sync");
return teeui::UIOption(uio);
}
inline MsgString str2MsgString(const string& s) {
return {s.c_str(), s.c_str() + s.size()};
}
template <typename T> inline MsgVector<T> vec2MsgVector(const vector<T>& v) {
return {v};
}
inline MsgVector<teeui::UIOption> vec2MsgVector(const vector<UIOption>& v) {
MsgVector<teeui::UIOption> result(v.size());
for (unsigned int i = 0; i < v.size(); ++i) {
result[i] = convertUIOption(v[i]);
}
return result;
}
} // namespace
const char* TrustyConfirmationUI::GetVirtioConsoleDevicePath() {
static char device_path[] = "/dev/hvc8";
return device_path;
}
TrustyConfirmationUI::TrustyConfirmationUI()
: listener_state_(ListenerState::None),
prompt_result_(IConfirmationUI::IGNORED), current_session_id_{10} {
host_fd_ = cuttlefish::SharedFD::Open(GetVirtioConsoleDevicePath(), O_RDWR);
CHECK(host_fd_->IsOpen()) << "ConfUI: " << GetVirtioConsoleDevicePath() << " is not open.";
CHECK(host_fd_->SetTerminalRaw() >= 0)
<< "ConfUI: " << GetVirtioConsoleDevicePath() << " fail in SetTerminalRaw()";
constexpr static const auto enable_confirmationui_property = "ro.boot.enable_confirmationui";
const auto arg = property_get_int32(enable_confirmationui_property, -1);
is_supported_vm_ = (arg == 1);
if (host_fd_->IsOpen()) {
auto fetching_cmd = [this]() { HostMessageFetcherLoop(); };
host_cmd_fetcher_thread_ = std::thread(fetching_cmd);
}
}
TrustyConfirmationUI::~TrustyConfirmationUI() {
if (host_fd_->IsOpen()) {
host_fd_->Close();
}
if (host_cmd_fetcher_thread_.joinable()) {
host_cmd_fetcher_thread_.join();
}
if (listener_state_ != ListenerState::None) {
callback_thread_.join();
}
}
void TrustyConfirmationUI::HostMessageFetcherLoop() {
while (true) {
if (!host_fd_->IsOpen()) {
// this happens when TrustyConfirmationUI is destroyed
ConfUiLog(ERROR) << "host_fd_ is not open";
return;
}
ConfUiLog(INFO) << "Trying to fetch command";
auto msg = cuttlefish::confui::RecvConfUiMsg(host_fd_);
ConfUiLog(INFO) << "RecvConfUiMsg() returned";
if (!msg) {
// virtio-console is broken for now
ConfUiLog(ERROR) << "received message was null";
return;
}
{
std::unique_lock<std::mutex> lk(current_session_lock_);
if (!current_session_ || msg->GetSessionId() != current_session_->GetSessionId()) {
if (!current_session_) {
ConfUiLog(ERROR) << "msg is received but session is null";
continue;
}
ConfUiLog(ERROR) << "session id mismatch, so ignored"
<< "Received for " << msg->GetSessionId()
<< " but currently running " << current_session_->GetSessionId();
continue;
}
current_session_->Push(std::move(msg));
}
listener_state_condv_.notify_all();
}
}
void TrustyConfirmationUI::RunSession(shared_ptr<IConfirmationResultCallback> resultCB,
string promptText, vector<uint8_t> extraData, string locale,
vector<UIOption> uiOptions) {
cuttlefish::SharedFD fd = host_fd_;
// ownership of the fd is passed to GuestSession
{
std::unique_lock<std::mutex> lk(current_session_lock_);
current_session_ = std::make_unique<GuestSession>(
current_session_id_, listener_state_, listener_state_lock_, listener_state_condv_, fd,
str2MsgString(promptText), vec2MsgVector(extraData), str2MsgString(locale),
vec2MsgVector(uiOptions));
}
auto [rc, msg, token] = current_session_->PromptUserConfirmation();
std::unique_lock<std::mutex> lock(listener_state_lock_); // for listener_state_
bool do_callback = (listener_state_ == ListenerState::Interactive ||
listener_state_ == ListenerState::SetupDone) &&
resultCB;
prompt_result_ = rc;
listener_state_ = ListenerState::Terminating;
lock.unlock();
if (do_callback) {
auto error = resultCB->result(prompt_result_, msg, token);
if (!error.isOk()) {
if (error.getExceptionCode() == EX_SERVICE_SPECIFIC) {
ConfUiLog(ERROR) << "Result callback failed error: "
<< error.getServiceSpecificError();
} else {
ConfUiLog(ERROR) << "Result callback failed error: " << error.getStatus();
}
}
ConfUiLog(INFO) << "Result callback returned.";
} else {
listener_state_condv_.notify_all();
}
}
// Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
// follow.
::ndk::ScopedAStatus TrustyConfirmationUI::promptUserConfirmation(
const shared_ptr<IConfirmationResultCallback>& resultCB, const vector<uint8_t>& promptTextBytes,
const vector<uint8_t>& extraData, const string& locale, const vector<UIOption>& uiOptions) {
std::unique_lock<std::mutex> stateLock(listener_state_lock_, std::defer_lock);
ConfUiLog(INFO) << "promptUserConfirmation is called";
string promptText(promptTextBytes.begin(), promptTextBytes.end());
if (!is_supported_vm_) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNIMPLEMENTED));
}
if (!stateLock.try_lock()) {
return ndk::ScopedAStatus(
AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING));
}
switch (listener_state_) {
case ListenerState::None:
break;
case ListenerState::Starting:
case ListenerState::SetupDone:
case ListenerState::Interactive:
return ndk::ScopedAStatus(
AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING));
case ListenerState::Terminating:
callback_thread_.join();
listener_state_ = ListenerState::None;
break;
default:
return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNEXPECTED));
}
assert(listener_state_ == ListenerState::None);
listener_state_ = ListenerState::Starting;
current_session_id_++;
auto worker = [this](const shared_ptr<IConfirmationResultCallback>& resultCB,
const string& promptText, const vector<uint8_t>& extraData,
const string& locale, const vector<UIOption>& uiOptions) {
RunSession(resultCB, promptText, extraData, locale, uiOptions);
};
callback_thread_ = std::thread(worker, resultCB, promptText, extraData, locale, uiOptions);
listener_state_condv_.wait(stateLock, [this] {
return listener_state_ == ListenerState::SetupDone ||
listener_state_ == ListenerState::Interactive ||
listener_state_ == ListenerState::Terminating;
});
if (listener_state_ == ListenerState::Terminating) {
callback_thread_.join();
listener_state_ = ListenerState::None;
if (prompt_result_ == IConfirmationUI::CANCELED) {
// VTS expects this
return ndk::ScopedAStatus::ok();
}
return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(prompt_result_));
}
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus
TrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& auth_token) {
ConfUiLog(INFO) << "deliverSecureInputEvent is called";
int rc = IConfirmationUI::IGNORED;
if (!is_supported_vm_) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNIMPLEMENTED));
}
{
std::unique_lock<std::mutex> lock(current_session_lock_);
if (!current_session_) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(rc));
}
rc = current_session_->DeliverSecureInputEvent(auth_token);
if (rc != IConfirmationUI::OK) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(rc));
}
}
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus TrustyConfirmationUI::abort() {
if (!is_supported_vm_) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNIMPLEMENTED));
}
std::unique_lock<std::mutex> lock(current_session_lock_);
if (!current_session_) {
return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::IGNORED));
}
current_session_->Abort();
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::confirmationui