253 lines
9.8 KiB
C++
253 lines
9.8 KiB
C++
/*
|
|
*
|
|
* 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
|