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