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