• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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