/* * * 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 namespace android { namespace hardware { namespace confirmationui { namespace V1_0 { namespace implementation { using ::teeui::MsgString; using ::teeui::MsgVector; using ::android::hardware::keymaster::V4_0::HardwareAuthToken; using TeeuiRc = ::teeui::ResponseCode; namespace { teeui::UIOption convertUIOption(UIOption uio) { static_assert(uint32_t(UIOption::AccessibilityInverted) == uint32_t(teeui::UIOption::AccessibilityInverted) && uint32_t(UIOption::AccessibilityMagnified) == uint32_t(teeui::UIOption::AccessibilityMagnified), "teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption " "are out of sync"); return teeui::UIOption(uio); } inline MsgString hidl2MsgString(const hidl_string& s) { return {s.c_str(), s.c_str() + s.size()}; } template inline MsgVector hidl2MsgVector(const hidl_vec& v) { return {v}; } inline MsgVector hidl2MsgVector(const hidl_vec& v) { MsgVector result(v.size()); for (unsigned int i = 0; i < v.size(); ++i) { result[i] = convertUIOption(v[i]); } return result; } } // namespace cuttlefish::SharedFD TrustyConfirmationUI::ConnectToHost() { using namespace std::chrono_literals; while (true) { auto host_fd = cuttlefish::SharedFD::VsockClient(2, host_vsock_port_, SOCK_STREAM); if (host_fd->IsOpen()) { ConfUiLog(INFO) << "Client connection is established"; return host_fd; } ConfUiLog(INFO) << "host service is not on. Sleep for 500 ms"; std::this_thread::sleep_for(500ms); } } TrustyConfirmationUI::TrustyConfirmationUI() : listener_state_(ListenerState::None), prompt_result_(ResponseCode::Ignored), host_vsock_port_{static_cast(property_get_int64( "ro.boot.vsock_confirmationui_port", 7700))}, current_session_id_{10} { ConfUiLog(INFO) << "Connecting to Confirmation UI host listening on port " << host_vsock_port_; host_fd_ = ConnectToHost(); auto fetching_cmd = [this]() { HostMessageFetcherLoop(); }; if (host_fd_->IsOpen()) { 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; } auto msg = cuttlefish::confui::RecvConfUiMsg(host_fd_); if (!msg) { // socket is broken for now return; } { std::unique_lock 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(sp resultCB, hidl_string promptText, hidl_vec extraData, hidl_string locale, hidl_vec uiOptions) { cuttlefish::SharedFD fd = host_fd_; // ownership of the fd is passed to GuestSession { std::unique_lock lk(current_session_lock_); current_session_ = std::make_unique( current_session_id_, listener_state_, listener_state_lock_, listener_state_condv_, fd, hidl2MsgString(promptText), hidl2MsgVector(extraData), hidl2MsgString(locale), hidl2MsgVector(uiOptions)); } auto [rc, msg, token] = current_session_->PromptUserConfirmation(); std::unique_lock 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()) { ConfUiLog(ERROR) << "Result callback failed " << error.description(); } ConfUiLog(INFO) << "Result callback returned."; } else { listener_state_condv_.notify_all(); } } // Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI // follow. Return TrustyConfirmationUI::promptUserConfirmation( const sp& resultCB, const hidl_string& promptText, const hidl_vec& extraData, const hidl_string& locale, const hidl_vec& uiOptions) { std::unique_lock stateLock(listener_state_lock_, std::defer_lock); ConfUiLog(INFO) << "promptUserConfirmation is called"; if (!stateLock.try_lock()) { return ResponseCode::OperationPending; } switch (listener_state_) { case ListenerState::None: break; case ListenerState::Starting: case ListenerState::SetupDone: case ListenerState::Interactive: return ResponseCode::OperationPending; case ListenerState::Terminating: callback_thread_.join(); listener_state_ = ListenerState::None; break; default: return ResponseCode::Unexpected; } assert(listener_state_ == ListenerState::None); listener_state_ = ListenerState::Starting; ConfUiLog(INFO) << "Per promptUserConfirmation, " << "an active TEE UI session starts"; current_session_id_++; auto worker = [this](const sp& resultCB, const hidl_string& promptText, const hidl_vec& extraData, const hidl_string& locale, const hidl_vec& 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_ == ResponseCode::Canceled) { // VTS expects this return ResponseCode::OK; } return prompt_result_; } return ResponseCode::OK; } Return TrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& auth_token) { ConfUiLog(INFO) << "deliverSecureInputEvent is called"; ResponseCode rc = ResponseCode::Ignored; { std::unique_lock lock(current_session_lock_); if (!current_session_) { return rc; } return current_session_->DeliverSecureInputEvent(auth_token); } } Return TrustyConfirmationUI::abort() { { std::unique_lock lock(current_session_lock_); if (!current_session_) { return Void(); } return current_session_->Abort(); } } android::sp createTrustyConfirmationUI() { return new TrustyConfirmationUI(); } } // namespace implementation } // namespace V1_0 } // namespace confirmationui } // namespace hardware } // namespace android