1 /*
2 *
3 * Copyright 2021, 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 "guest_session.h"
19
20 #include <future>
21
22 namespace android {
23 namespace hardware {
24 namespace confirmationui {
25 namespace V1_0 {
26 namespace implementation {
27 using TeeuiRc = teeui::ResponseCode;
28
PromptUserConfirmation()29 GuestSession::ResultTriple GuestSession::PromptUserConfirmation() {
30 std::unique_lock<std::mutex> stateLock(listener_state_lock_);
31 /*
32 * This is the main listener thread function. The listener thread life cycle
33 * is equivalent to the life cycle of a single confirmation request. The life
34 * cycle is divided in four phases.
35 * * The starting phase:
36 * * Drives the cuttlefish confirmation UI session on the host side, too
37 *
38 * Note: During the starting phase the hwbinder service thread is blocked and
39 * waiting for possible Errors. If the setup phase concludes successfully, the
40 * hwbinder service thread gets unblocked and returns successfully. Errors
41 * that occur after the first phase are delivered by callback interface.
42 *
43 * For cuttlefish, it means that the guest will conduct a blocking wait for
44 * an ack to kStart.
45 *
46 * * The 2nd phase - non interactive phase
47 * * After a grace period:
48 * * guest will pick up cuttlefish host's ack to kStart
49 *
50 * * The 3rd phase - interactive phase
51 * * We wait to any external event
52 * * Abort
53 * * Secure user input asserted
54 * * The result is fetched from the TA.
55 *
56 * * The 4th phase - cleanup
57 * * Sending the kStop command to the cuttlefish host, and wait for ack
58 */
59
60 GuestSession::ResultTriple error;
61 auto& error_rc = std::get<ResponseCode>(error);
62 error_rc = ResponseCode::SystemError;
63
64 CHECK(listener_state_ == ListenerState::Starting) << "ListenerState should be Starting";
65
66 // initiate prompt
67 ConfUiLog(INFO) << "Initiating prompt";
68 const std::uint32_t payload_lower_bound =
69 static_cast<std::uint32_t>(prompt_text_.size() + extra_data_.size());
70 const std::uint32_t upper_bound =
71 static_cast<std::uint32_t>(cuttlefish::confui::kMaxMessageLength);
72 if (payload_lower_bound > upper_bound) {
73 ConfUiLog(INFO) << "UI message too long to send to the host";
74 // message is too long anyway, and don't send it to the host
75 error_rc = ResponseCode::UIErrorMessageTooLong;
76 return error;
77 }
78 SerializedSend(cuttlefish::confui::SendStartCmd, host_fd_, session_name_, prompt_text_,
79 extra_data_, locale_, ui_options_);
80 ConfUiLog(INFO) << "Session " << GetSessionId() << " started on both the guest and the host";
81
82 auto clean_up_and_get_first = [&]() -> std::unique_ptr<ConfUiMessage> {
83 // blocking wait to get the first msg that belongs to this session
84 while (true) {
85 auto first_curr_session_msg = incoming_msg_queue_.Pop();
86 if (!first_curr_session_msg ||
87 first_curr_session_msg->GetSessionId() != GetSessionId()) {
88 continue;
89 }
90 return std::move(first_curr_session_msg);
91 }
92 };
93
94 /*
95 * Unconditionally wait ack, or host abort
96 *
97 * First couple of messages could be from the previous session.
98 * We should clear them up.
99 *
100 * Even though the guest HAL sends kAbort to the host, the kAbort
101 * does not happen immediately. Between the incoming_msg_queue_.FlushAll()
102 * and the actual abort on the host, there could still be messages
103 * sent from the host to the guest. As these lines are the first read
104 * for the current session, we clear up the preceding messages
105 * from the previous session until we see the message for the current
106 * session.
107 *
108 * Note that abort() call puts the Abort command in the queue. So,
109 * it will also show up in incoming_msg_queue_
110 *
111 */
112 auto first_msg = std::move(clean_up_and_get_first());
113
114 cuttlefish::confui::ConfUiAckMessage& start_ack_msg =
115 static_cast<cuttlefish::confui::ConfUiAckMessage&>(*first_msg);
116 if (!start_ack_msg.IsSuccess()) {
117 // handle errors: MALFORMED_UTF8 or Message too long
118 const std::string error_msg = start_ack_msg.GetStatusMessage();
119 if (error_msg == cuttlefish::confui::HostError::kMessageTooLongError) {
120 ConfUiLog(ERROR) << "Message + Extra data + Meta info were too long";
121 error_rc = ResponseCode::UIErrorMessageTooLong;
122 }
123 if (error_msg == cuttlefish::confui::HostError::kIncorrectUTF8) {
124 ConfUiLog(ERROR) << "Message is incorrectly UTF-encoded";
125 error_rc = ResponseCode::UIErrorMalformedUTF8Encoding;
126 }
127 return error;
128 }
129
130 // ############################## Start 2nd Phase #############################################
131 listener_state_ = ListenerState::SetupDone;
132 ConfUiLog(INFO) << "Transition to SetupDone";
133 stateLock.unlock();
134 listener_state_condv_.notify_all();
135
136 // cuttlefish does not need the second phase to implement HAL APIs
137 // input was already prepared before the confirmation UI screen was rendered
138
139 // ############################## Start 3rd Phase - interactive phase #########################
140 stateLock.lock();
141 listener_state_ = ListenerState::Interactive;
142 ConfUiLog(INFO) << "Transition to Interactive";
143 stateLock.unlock();
144 listener_state_condv_.notify_all();
145
146 // give deliverSecureInputEvent a chance to interrupt
147
148 // wait for an input but should not block deliverSecureInputEvent or Abort
149 // Thus, it should not hold the stateLock
150 std::mutex input_ready_mtx;
151 std::condition_variable input_ready_cv_;
152 std::unique_lock<std::mutex> input_ready_lock(input_ready_mtx);
153 bool input_ready = false;
154 auto wait_input_and_signal = [&]() -> std::unique_ptr<ConfUiMessage> {
155 auto msg = incoming_msg_queue_.Pop();
156 {
157 std::unique_lock<std::mutex> lock(input_ready_mtx);
158 input_ready = true;
159 input_ready_cv_.notify_one();
160 }
161 return msg;
162 };
163 auto input_and_signal_future = std::async(std::launch::async, wait_input_and_signal);
164 input_ready_cv_.wait(input_ready_lock, [&]() { return input_ready; });
165 // now an input is ready, so let's acquire the stateLock
166
167 stateLock.lock();
168 auto user_or_abort = input_and_signal_future.get();
169
170 if (user_or_abort->GetType() == cuttlefish::confui::ConfUiCmd::kAbort) {
171 ConfUiLog(ERROR) << "Abort called or the user/host aborted"
172 << " while waiting user response";
173 return {ResponseCode::Aborted, {}, {}};
174 }
175 if (user_or_abort->GetType() == cuttlefish::confui::ConfUiCmd::kCliAck) {
176 auto& ack_msg = static_cast<cuttlefish::confui::ConfUiAckMessage&>(*user_or_abort);
177 if (ack_msg.IsSuccess()) {
178 ConfUiLog(ERROR) << "When host failed, it is supposed to send "
179 << "kCliAck with fail, but this is kCliAck with success";
180 }
181 error_rc = ResponseCode::SystemError;
182 return error;
183 }
184 cuttlefish::confui::ConfUiCliResponseMessage& user_response =
185 static_cast<cuttlefish::confui::ConfUiCliResponseMessage&>(*user_or_abort);
186
187 // pick, see if it is response, abort cmd
188 // handle abort or error response here
189 ConfUiLog(INFO) << "Making up the result";
190
191 // make up the result triple
192 if (user_response.GetResponse() == cuttlefish::confui::UserResponse::kCancel) {
193 SerializedSend(cuttlefish::confui::SendStopCmd, host_fd_, GetSessionId());
194 return {ResponseCode::Canceled, {}, {}};
195 }
196
197 if (user_response.GetResponse() != cuttlefish::confui::UserResponse::kConfirm) {
198 ConfUiLog(ERROR) << "Unexpected user response that is " << user_response.GetResponse();
199 return error;
200 }
201 SerializedSend(cuttlefish::confui::SendStopCmd, host_fd_, GetSessionId());
202 // ############################## Start 4th Phase - cleanup ##################################
203 return {ResponseCode::OK, user_response.GetMessage(), user_response.GetSign()};
204 }
205
DeliverSecureInputEvent(const android::hardware::keymaster::V4_0::HardwareAuthToken & auth_token)206 Return<ResponseCode> GuestSession::DeliverSecureInputEvent(
207 const android::hardware::keymaster::V4_0::HardwareAuthToken& auth_token) {
208 ResponseCode rc = ResponseCode::Ignored;
209 {
210 /*
211 * deliverSecureInputEvent is only used by the VTS test to mock human input. A correct
212 * implementation responds with a mock confirmation token signed with a test key. The
213 * problem is that the non interactive grace period was not formalized in the HAL spec,
214 * so that the VTS test does not account for the grace period. (It probably should.)
215 * This means we can only pass the VTS test if we block until the grace period is over
216 * (SetupDone -> Interactive) before we deliver the input event.
217 *
218 * The true secure input is delivered by a different mechanism and gets ignored -
219 * not queued - until the grace period is over.
220 *
221 */
222 std::unique_lock<std::mutex> stateLock(listener_state_lock_);
223 listener_state_condv_.wait(stateLock,
224 [this] { return listener_state_ != ListenerState::SetupDone; });
225 if (listener_state_ != ListenerState::Interactive) return ResponseCode::Ignored;
226 if (static_cast<TestModeCommands>(auth_token.challenge) == TestModeCommands::OK_EVENT) {
227 SerializedSend(cuttlefish::confui::SendUserSelection, host_fd_, GetSessionId(),
228 cuttlefish::confui::UserResponse::kConfirm);
229 } else {
230 SerializedSend(cuttlefish::confui::SendUserSelection, host_fd_, GetSessionId(),
231 cuttlefish::confui::UserResponse::kCancel);
232 }
233 rc = ResponseCode::OK;
234 }
235 listener_state_condv_.notify_all();
236 // VTS test expect an OK response if the event was successfully delivered.
237 // But since the TA returns the callback response now, we have to translate
238 // Canceled into OK. Canceled is only returned if the delivered event canceled
239 // the operation, which means that the event was successfully delivered. Thus
240 // we return OK.
241 if (rc == ResponseCode::Canceled) return ResponseCode::OK;
242 return rc;
243 }
244
Abort()245 Return<void> GuestSession::Abort() {
246 {
247 std::unique_lock<std::mutex> stateLock(listener_state_lock_);
248 if (listener_state_ == ListenerState::SetupDone ||
249 listener_state_ == ListenerState::Interactive) {
250 if (host_fd_->IsOpen()) {
251 SerializedSend(cuttlefish::confui::SendAbortCmd, host_fd_, GetSessionId());
252 }
253 using cuttlefish::confui::ConfUiAbortMessage;
254 auto local_abort_cmd = std::make_unique<ConfUiAbortMessage>(GetSessionId());
255 incoming_msg_queue_.Push(std::move(local_abort_cmd));
256 }
257 }
258 listener_state_condv_.notify_all();
259 return Void();
260 }
261 } // namespace implementation
262 } // namespace V1_0
263 } // namespace confirmationui
264 } // namespace hardware
265 } // namespace android
266