1 /*
2 *
3 * Copyright 2019, The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 #include "TrustyConfirmationUI.h"
19
20 #include <android/binder_manager.h>
21 #include <cutils/properties.h>
22
23 namespace aidl::android::hardware::confirmationui {
24 using ::teeui::MsgString;
25 using ::teeui::MsgVector;
26 using TeeuiRc = ::teeui::ResponseCode;
27
28 namespace {
convertUIOption(UIOption uio)29 teeui::UIOption convertUIOption(UIOption uio) {
30 static_assert(uint32_t(UIOption::ACCESSIBILITY_INVERTED) ==
31 uint32_t(teeui::UIOption::AccessibilityInverted) &&
32 uint32_t(UIOption::ACCESSIBILITY_MAGNIFIED) ==
33 uint32_t(teeui::UIOption::AccessibilityMagnified),
34 "teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption "
35 "are out of sync");
36 return teeui::UIOption(uio);
37 }
38
str2MsgString(const string & s)39 inline MsgString str2MsgString(const string& s) {
40 return {s.c_str(), s.c_str() + s.size()};
41 }
vec2MsgVector(const vector<T> & v)42 template <typename T> inline MsgVector<T> vec2MsgVector(const vector<T>& v) {
43 return {v};
44 }
45
vec2MsgVector(const vector<UIOption> & v)46 inline MsgVector<teeui::UIOption> vec2MsgVector(const vector<UIOption>& v) {
47 MsgVector<teeui::UIOption> result(v.size());
48 for (unsigned int i = 0; i < v.size(); ++i) {
49 result[i] = convertUIOption(v[i]);
50 }
51 return result;
52 }
53 } // namespace
54
GetVirtioConsoleDevicePath()55 const char* TrustyConfirmationUI::GetVirtioConsoleDevicePath() {
56 static char device_path[] = "/dev/hvc8";
57 return device_path;
58 }
59
TrustyConfirmationUI()60 TrustyConfirmationUI::TrustyConfirmationUI()
61 : listener_state_(ListenerState::None),
62 prompt_result_(IConfirmationUI::IGNORED), current_session_id_{10} {
63 host_fd_ = cuttlefish::SharedFD::Open(GetVirtioConsoleDevicePath(), O_RDWR);
64 CHECK(host_fd_->IsOpen()) << "ConfUI: " << GetVirtioConsoleDevicePath() << " is not open.";
65 CHECK(host_fd_->SetTerminalRaw() >= 0)
66 << "ConfUI: " << GetVirtioConsoleDevicePath() << " fail in SetTerminalRaw()";
67
68 constexpr static const auto enable_confirmationui_property = "ro.boot.enable_confirmationui";
69 const auto arg = property_get_int32(enable_confirmationui_property, -1);
70 is_supported_vm_ = (arg == 1);
71
72 if (host_fd_->IsOpen()) {
__anon7804b5ab0202() 73 auto fetching_cmd = [this]() { HostMessageFetcherLoop(); };
74 host_cmd_fetcher_thread_ = std::thread(fetching_cmd);
75 }
76 }
77
~TrustyConfirmationUI()78 TrustyConfirmationUI::~TrustyConfirmationUI() {
79 if (host_fd_->IsOpen()) {
80 host_fd_->Close();
81 }
82 if (host_cmd_fetcher_thread_.joinable()) {
83 host_cmd_fetcher_thread_.join();
84 }
85
86 if (listener_state_ != ListenerState::None) {
87 callback_thread_.join();
88 }
89 }
90
HostMessageFetcherLoop()91 void TrustyConfirmationUI::HostMessageFetcherLoop() {
92 while (true) {
93 if (!host_fd_->IsOpen()) {
94 // this happens when TrustyConfirmationUI is destroyed
95 ConfUiLog(ERROR) << "host_fd_ is not open";
96 return;
97 }
98 ConfUiLog(INFO) << "Trying to fetch command";
99 auto msg = cuttlefish::confui::RecvConfUiMsg(host_fd_);
100 ConfUiLog(INFO) << "RecvConfUiMsg() returned";
101 if (!msg) {
102 // virtio-console is broken for now
103 ConfUiLog(ERROR) << "received message was null";
104 return;
105 }
106 {
107 std::unique_lock<std::mutex> lk(current_session_lock_);
108 if (!current_session_ || msg->GetSessionId() != current_session_->GetSessionId()) {
109 if (!current_session_) {
110 ConfUiLog(ERROR) << "msg is received but session is null";
111 continue;
112 }
113 ConfUiLog(ERROR) << "session id mismatch, so ignored"
114 << "Received for " << msg->GetSessionId()
115 << " but currently running " << current_session_->GetSessionId();
116 continue;
117 }
118 current_session_->Push(std::move(msg));
119 }
120 listener_state_condv_.notify_all();
121 }
122 }
123
RunSession(shared_ptr<IConfirmationResultCallback> resultCB,string promptText,vector<uint8_t> extraData,string locale,vector<UIOption> uiOptions)124 void TrustyConfirmationUI::RunSession(shared_ptr<IConfirmationResultCallback> resultCB,
125 string promptText, vector<uint8_t> extraData, string locale,
126 vector<UIOption> uiOptions) {
127 cuttlefish::SharedFD fd = host_fd_;
128 // ownership of the fd is passed to GuestSession
129 {
130 std::unique_lock<std::mutex> lk(current_session_lock_);
131 current_session_ = std::make_unique<GuestSession>(
132 current_session_id_, listener_state_, listener_state_lock_, listener_state_condv_, fd,
133 str2MsgString(promptText), vec2MsgVector(extraData), str2MsgString(locale),
134 vec2MsgVector(uiOptions));
135 }
136
137 auto [rc, msg, token] = current_session_->PromptUserConfirmation();
138
139 std::unique_lock<std::mutex> lock(listener_state_lock_); // for listener_state_
140 bool do_callback = (listener_state_ == ListenerState::Interactive ||
141 listener_state_ == ListenerState::SetupDone) &&
142 resultCB;
143 prompt_result_ = rc;
144 listener_state_ = ListenerState::Terminating;
145 lock.unlock();
146 if (do_callback) {
147 auto error = resultCB->result(prompt_result_, msg, token);
148 if (!error.isOk()) {
149 if (error.getExceptionCode() == EX_SERVICE_SPECIFIC) {
150 ConfUiLog(ERROR) << "Result callback failed error: "
151 << error.getServiceSpecificError();
152 } else {
153 ConfUiLog(ERROR) << "Result callback failed error: " << error.getStatus();
154 }
155 }
156 ConfUiLog(INFO) << "Result callback returned.";
157 } else {
158 listener_state_condv_.notify_all();
159 }
160 }
161
162 // Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
163 // follow.
promptUserConfirmation(const shared_ptr<IConfirmationResultCallback> & resultCB,const vector<uint8_t> & promptTextBytes,const vector<uint8_t> & extraData,const string & locale,const vector<UIOption> & uiOptions)164 ::ndk::ScopedAStatus TrustyConfirmationUI::promptUserConfirmation(
165 const shared_ptr<IConfirmationResultCallback>& resultCB, const vector<uint8_t>& promptTextBytes,
166 const vector<uint8_t>& extraData, const string& locale, const vector<UIOption>& uiOptions) {
167 std::unique_lock<std::mutex> stateLock(listener_state_lock_, std::defer_lock);
168 ConfUiLog(INFO) << "promptUserConfirmation is called";
169 string promptText(promptTextBytes.begin(), promptTextBytes.end());
170 if (!is_supported_vm_) {
171 return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNIMPLEMENTED));
172 }
173 if (!stateLock.try_lock()) {
174 return ndk::ScopedAStatus(
175 AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING));
176 }
177 switch (listener_state_) {
178 case ListenerState::None:
179 break;
180 case ListenerState::Starting:
181 case ListenerState::SetupDone:
182 case ListenerState::Interactive:
183 return ndk::ScopedAStatus(
184 AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING));
185 case ListenerState::Terminating:
186 callback_thread_.join();
187 listener_state_ = ListenerState::None;
188 break;
189 default:
190 return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNEXPECTED));
191 }
192 assert(listener_state_ == ListenerState::None);
193 listener_state_ = ListenerState::Starting;
194
195 current_session_id_++;
196 auto worker = [this](const shared_ptr<IConfirmationResultCallback>& resultCB,
197 const string& promptText, const vector<uint8_t>& extraData,
198 const string& locale, const vector<UIOption>& uiOptions) {
199 RunSession(resultCB, promptText, extraData, locale, uiOptions);
200 };
201 callback_thread_ = std::thread(worker, resultCB, promptText, extraData, locale, uiOptions);
202
203 listener_state_condv_.wait(stateLock, [this] {
204 return listener_state_ == ListenerState::SetupDone ||
205 listener_state_ == ListenerState::Interactive ||
206 listener_state_ == ListenerState::Terminating;
207 });
208 if (listener_state_ == ListenerState::Terminating) {
209 callback_thread_.join();
210 listener_state_ = ListenerState::None;
211 if (prompt_result_ == IConfirmationUI::CANCELED) {
212 // VTS expects this
213 return ndk::ScopedAStatus::ok();
214 }
215 return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(prompt_result_));
216 }
217 return ndk::ScopedAStatus::ok();
218 }
219
220 ::ndk::ScopedAStatus
deliverSecureInputEvent(const HardwareAuthToken & auth_token)221 TrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& auth_token) {
222 ConfUiLog(INFO) << "deliverSecureInputEvent is called";
223 int rc = IConfirmationUI::IGNORED;
224 if (!is_supported_vm_) {
225 return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNIMPLEMENTED));
226 }
227 {
228 std::unique_lock<std::mutex> lock(current_session_lock_);
229 if (!current_session_) {
230 return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(rc));
231 }
232 rc = current_session_->DeliverSecureInputEvent(auth_token);
233 if (rc != IConfirmationUI::OK) {
234 return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(rc));
235 }
236 }
237 return ndk::ScopedAStatus::ok();
238 }
239
abort()240 ::ndk::ScopedAStatus TrustyConfirmationUI::abort() {
241 if (!is_supported_vm_) {
242 return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNIMPLEMENTED));
243 }
244 std::unique_lock<std::mutex> lock(current_session_lock_);
245 if (!current_session_) {
246 return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::IGNORED));
247 }
248 current_session_->Abort();
249 return ndk::ScopedAStatus::ok();
250 }
251
252 } // namespace aidl::android::hardware::confirmationui
253