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 <cutils/properties.h>
21
22 namespace android {
23 namespace hardware {
24 namespace confirmationui {
25 namespace V1_0 {
26 namespace implementation {
27
28 using ::teeui::MsgString;
29 using ::teeui::MsgVector;
30 using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
31 using TeeuiRc = ::teeui::ResponseCode;
32
33 namespace {
convertUIOption(UIOption uio)34 teeui::UIOption convertUIOption(UIOption uio) {
35 static_assert(uint32_t(UIOption::AccessibilityInverted) ==
36 uint32_t(teeui::UIOption::AccessibilityInverted) &&
37 uint32_t(UIOption::AccessibilityMagnified) ==
38 uint32_t(teeui::UIOption::AccessibilityMagnified),
39 "teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption "
40 "are out of sync");
41 return teeui::UIOption(uio);
42 }
43
hidl2MsgString(const hidl_string & s)44 inline MsgString hidl2MsgString(const hidl_string& s) {
45 return {s.c_str(), s.c_str() + s.size()};
46 }
hidl2MsgVector(const hidl_vec<T> & v)47 template <typename T> inline MsgVector<T> hidl2MsgVector(const hidl_vec<T>& v) {
48 return {v};
49 }
50
hidl2MsgVector(const hidl_vec<UIOption> & v)51 inline MsgVector<teeui::UIOption> hidl2MsgVector(const hidl_vec<UIOption>& v) {
52 MsgVector<teeui::UIOption> result(v.size());
53 for (unsigned int i = 0; i < v.size(); ++i) {
54 result[i] = convertUIOption(v[i]);
55 }
56 return result;
57 }
58 } // namespace
59
ConnectToHost()60 cuttlefish::SharedFD TrustyConfirmationUI::ConnectToHost() {
61 using namespace std::chrono_literals;
62 while (true) {
63 auto host_fd = cuttlefish::SharedFD::VsockClient(2, host_vsock_port_, SOCK_STREAM);
64 if (host_fd->IsOpen()) {
65 ConfUiLog(INFO) << "Client connection is established";
66 return host_fd;
67 }
68 ConfUiLog(INFO) << "host service is not on. Sleep for 500 ms";
69 std::this_thread::sleep_for(500ms);
70 }
71 }
72
TrustyConfirmationUI()73 TrustyConfirmationUI::TrustyConfirmationUI()
74 : listener_state_(ListenerState::None),
75 prompt_result_(ResponseCode::Ignored), host_vsock_port_{static_cast<int>(property_get_int64(
76 "ro.boot.vsock_confirmationui_port", 7700))},
77 current_session_id_{10} {
78 ConfUiLog(INFO) << "Connecting to Confirmation UI host listening on port " << host_vsock_port_;
79 host_fd_ = ConnectToHost();
__anone5301d570202() 80 auto fetching_cmd = [this]() { HostMessageFetcherLoop(); };
81 if (host_fd_->IsOpen()) {
82 host_cmd_fetcher_thread_ = std::thread(fetching_cmd);
83 }
84 }
85
~TrustyConfirmationUI()86 TrustyConfirmationUI::~TrustyConfirmationUI() {
87 if (host_fd_->IsOpen()) {
88 host_fd_->Close();
89 }
90 if (host_cmd_fetcher_thread_.joinable()) {
91 host_cmd_fetcher_thread_.join();
92 }
93
94 if (listener_state_ != ListenerState::None) {
95 callback_thread_.join();
96 }
97 }
98
HostMessageFetcherLoop()99 void TrustyConfirmationUI::HostMessageFetcherLoop() {
100 while (true) {
101 if (!host_fd_->IsOpen()) {
102 // this happens when TrustyConfirmationUI is destroyed
103 ConfUiLog(ERROR) << "host_fd_ is not open";
104 return;
105 }
106 auto msg = cuttlefish::confui::RecvConfUiMsg(host_fd_);
107 if (!msg) {
108 // socket is broken for now
109 return;
110 }
111 {
112 std::unique_lock<std::mutex> lk(current_session_lock_);
113 if (!current_session_ || msg->GetSessionId() != current_session_->GetSessionId()) {
114 if (!current_session_) {
115 ConfUiLog(ERROR) << "msg is received but session is null";
116 continue;
117 }
118 ConfUiLog(ERROR) << "session id mismatch, so ignored"
119 << "Received for " << msg->GetSessionId()
120 << " but currently running " << current_session_->GetSessionId();
121 continue;
122 }
123 current_session_->Push(std::move(msg));
124 }
125 listener_state_condv_.notify_all();
126 }
127 }
128
RunSession(sp<IConfirmationResultCallback> resultCB,hidl_string promptText,hidl_vec<uint8_t> extraData,hidl_string locale,hidl_vec<UIOption> uiOptions)129 void TrustyConfirmationUI::RunSession(sp<IConfirmationResultCallback> resultCB,
130 hidl_string promptText, hidl_vec<uint8_t> extraData,
131 hidl_string locale, hidl_vec<UIOption> uiOptions) {
132 cuttlefish::SharedFD fd = host_fd_;
133 // ownership of the fd is passed to GuestSession
134 {
135 std::unique_lock<std::mutex> lk(current_session_lock_);
136 current_session_ = std::make_unique<GuestSession>(
137 current_session_id_, listener_state_, listener_state_lock_, listener_state_condv_, fd,
138 hidl2MsgString(promptText), hidl2MsgVector(extraData), hidl2MsgString(locale),
139 hidl2MsgVector(uiOptions));
140 }
141
142 auto [rc, msg, token] = current_session_->PromptUserConfirmation();
143
144 std::unique_lock<std::mutex> lock(listener_state_lock_); // for listener_state_
145 bool do_callback = (listener_state_ == ListenerState::Interactive ||
146 listener_state_ == ListenerState::SetupDone) &&
147 resultCB;
148 prompt_result_ = rc;
149 listener_state_ = ListenerState::Terminating;
150 lock.unlock();
151 if (do_callback) {
152 auto error = resultCB->result(prompt_result_, msg, token);
153 if (!error.isOk()) {
154 ConfUiLog(ERROR) << "Result callback failed " << error.description();
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 sp<IConfirmationResultCallback> & resultCB,const hidl_string & promptText,const hidl_vec<uint8_t> & extraData,const hidl_string & locale,const hidl_vec<UIOption> & uiOptions)164 Return<ResponseCode> TrustyConfirmationUI::promptUserConfirmation(
165 const sp<IConfirmationResultCallback>& resultCB, const hidl_string& promptText,
166 const hidl_vec<uint8_t>& extraData, const hidl_string& locale,
167 const hidl_vec<UIOption>& uiOptions) {
168 std::unique_lock<std::mutex> stateLock(listener_state_lock_, std::defer_lock);
169 ConfUiLog(INFO) << "promptUserConfirmation is called";
170
171 if (!stateLock.try_lock()) {
172 return ResponseCode::OperationPending;
173 }
174 switch (listener_state_) {
175 case ListenerState::None:
176 break;
177 case ListenerState::Starting:
178 case ListenerState::SetupDone:
179 case ListenerState::Interactive:
180 return ResponseCode::OperationPending;
181 case ListenerState::Terminating:
182 callback_thread_.join();
183 listener_state_ = ListenerState::None;
184 break;
185 default:
186 return ResponseCode::Unexpected;
187 }
188 assert(listener_state_ == ListenerState::None);
189 listener_state_ = ListenerState::Starting;
190 ConfUiLog(INFO) << "Per promptUserConfirmation, "
191 << "an active TEE UI session starts";
192 current_session_id_++;
193 auto worker = [this](const sp<IConfirmationResultCallback>& resultCB,
194 const hidl_string& promptText, const hidl_vec<uint8_t>& extraData,
195 const hidl_string& locale, const hidl_vec<UIOption>& uiOptions) {
196 RunSession(resultCB, promptText, extraData, locale, uiOptions);
197 };
198 callback_thread_ = std::thread(worker, resultCB, promptText, extraData, locale, uiOptions);
199
200 listener_state_condv_.wait(stateLock, [this] {
201 return listener_state_ == ListenerState::SetupDone ||
202 listener_state_ == ListenerState::Interactive ||
203 listener_state_ == ListenerState::Terminating;
204 });
205 if (listener_state_ == ListenerState::Terminating) {
206 callback_thread_.join();
207 listener_state_ = ListenerState::None;
208 if (prompt_result_ == ResponseCode::Canceled) {
209 // VTS expects this
210 return ResponseCode::OK;
211 }
212 return prompt_result_;
213 }
214 return ResponseCode::OK;
215 }
216
217 Return<ResponseCode>
deliverSecureInputEvent(const HardwareAuthToken & auth_token)218 TrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& auth_token) {
219 ConfUiLog(INFO) << "deliverSecureInputEvent is called";
220 ResponseCode rc = ResponseCode::Ignored;
221 {
222 std::unique_lock<std::mutex> lock(current_session_lock_);
223 if (!current_session_) {
224 return rc;
225 }
226 return current_session_->DeliverSecureInputEvent(auth_token);
227 }
228 }
229
abort()230 Return<void> TrustyConfirmationUI::abort() {
231 {
232 std::unique_lock<std::mutex> lock(current_session_lock_);
233 if (!current_session_) {
234 return Void();
235 }
236 return current_session_->Abort();
237 }
238 }
239
createTrustyConfirmationUI()240 android::sp<IConfirmationUI> createTrustyConfirmationUI() {
241 return new TrustyConfirmationUI();
242 }
243
244 } // namespace implementation
245 } // namespace V1_0
246 } // namespace confirmationui
247 } // namespace hardware
248 } // namespace android
249