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 <android/binder_manager.h>
21 #include <cutils/properties.h>
22 
23 namespace aidl::android::hardware::confirmationui {
24 using ::teeui::MsgString;
25 using ::teeui::MsgVector;
26 using TeeuiRc = ::teeui::ResponseCode;
27 
28 namespace {
convertUIOption(UIOption uio)29 teeui::UIOption convertUIOption(UIOption uio) {
30     static_assert(uint32_t(UIOption::ACCESSIBILITY_INVERTED) ==
31                           uint32_t(teeui::UIOption::AccessibilityInverted) &&
32                       uint32_t(UIOption::ACCESSIBILITY_MAGNIFIED) ==
33                           uint32_t(teeui::UIOption::AccessibilityMagnified),
34                   "teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption "
35                   "are out of sync");
36     return teeui::UIOption(uio);
37 }
38 
str2MsgString(const string & s)39 inline MsgString str2MsgString(const string& s) {
40     return {s.c_str(), s.c_str() + s.size()};
41 }
vec2MsgVector(const vector<T> & v)42 template <typename T> inline MsgVector<T> vec2MsgVector(const vector<T>& v) {
43     return {v};
44 }
45 
vec2MsgVector(const vector<UIOption> & v)46 inline MsgVector<teeui::UIOption> vec2MsgVector(const vector<UIOption>& v) {
47     MsgVector<teeui::UIOption> result(v.size());
48     for (unsigned int i = 0; i < v.size(); ++i) {
49         result[i] = convertUIOption(v[i]);
50     }
51     return result;
52 }
53 }  // namespace
54 
GetVirtioConsoleDevicePath()55 const char* TrustyConfirmationUI::GetVirtioConsoleDevicePath() {
56     static char device_path[] = "/dev/hvc8";
57     return device_path;
58 }
59 
TrustyConfirmationUI()60 TrustyConfirmationUI::TrustyConfirmationUI()
61     : listener_state_(ListenerState::None),
62       prompt_result_(IConfirmationUI::IGNORED), current_session_id_{10} {
63     host_fd_ = cuttlefish::SharedFD::Open(GetVirtioConsoleDevicePath(), O_RDWR);
64     CHECK(host_fd_->IsOpen()) << "ConfUI: " << GetVirtioConsoleDevicePath() << " is not open.";
65     CHECK(host_fd_->SetTerminalRaw() >= 0)
66         << "ConfUI: " << GetVirtioConsoleDevicePath() << " fail in SetTerminalRaw()";
67 
68     constexpr static const auto enable_confirmationui_property = "ro.boot.enable_confirmationui";
69     const auto arg = property_get_int32(enable_confirmationui_property, -1);
70     is_supported_vm_ = (arg == 1);
71 
72     if (host_fd_->IsOpen()) {
__anon7804b5ab0202() 73         auto fetching_cmd = [this]() { HostMessageFetcherLoop(); };
74         host_cmd_fetcher_thread_ = std::thread(fetching_cmd);
75     }
76 }
77 
~TrustyConfirmationUI()78 TrustyConfirmationUI::~TrustyConfirmationUI() {
79     if (host_fd_->IsOpen()) {
80         host_fd_->Close();
81     }
82     if (host_cmd_fetcher_thread_.joinable()) {
83         host_cmd_fetcher_thread_.join();
84     }
85 
86     if (listener_state_ != ListenerState::None) {
87         callback_thread_.join();
88     }
89 }
90 
HostMessageFetcherLoop()91 void TrustyConfirmationUI::HostMessageFetcherLoop() {
92     while (true) {
93         if (!host_fd_->IsOpen()) {
94             // this happens when TrustyConfirmationUI is destroyed
95             ConfUiLog(ERROR) << "host_fd_ is not open";
96             return;
97         }
98         ConfUiLog(INFO) << "Trying to fetch command";
99         auto msg = cuttlefish::confui::RecvConfUiMsg(host_fd_);
100         ConfUiLog(INFO) << "RecvConfUiMsg() returned";
101         if (!msg) {
102             // virtio-console is broken for now
103             ConfUiLog(ERROR) << "received message was null";
104             return;
105         }
106         {
107             std::unique_lock<std::mutex> lk(current_session_lock_);
108             if (!current_session_ || msg->GetSessionId() != current_session_->GetSessionId()) {
109                 if (!current_session_) {
110                     ConfUiLog(ERROR) << "msg is received but session is null";
111                     continue;
112                 }
113                 ConfUiLog(ERROR) << "session id mismatch, so ignored"
114                                  << "Received for " << msg->GetSessionId()
115                                  << " but currently running " << current_session_->GetSessionId();
116                 continue;
117             }
118             current_session_->Push(std::move(msg));
119         }
120         listener_state_condv_.notify_all();
121     }
122 }
123 
RunSession(shared_ptr<IConfirmationResultCallback> resultCB,string promptText,vector<uint8_t> extraData,string locale,vector<UIOption> uiOptions)124 void TrustyConfirmationUI::RunSession(shared_ptr<IConfirmationResultCallback> resultCB,
125                                       string promptText, vector<uint8_t> extraData, string locale,
126                                       vector<UIOption> uiOptions) {
127     cuttlefish::SharedFD fd = host_fd_;
128     // ownership of the fd is passed to GuestSession
129     {
130         std::unique_lock<std::mutex> lk(current_session_lock_);
131         current_session_ = std::make_unique<GuestSession>(
132             current_session_id_, listener_state_, listener_state_lock_, listener_state_condv_, fd,
133             str2MsgString(promptText), vec2MsgVector(extraData), str2MsgString(locale),
134             vec2MsgVector(uiOptions));
135     }
136 
137     auto [rc, msg, token] = current_session_->PromptUserConfirmation();
138 
139     std::unique_lock<std::mutex> lock(listener_state_lock_);  // for listener_state_
140     bool do_callback = (listener_state_ == ListenerState::Interactive ||
141                         listener_state_ == ListenerState::SetupDone) &&
142                        resultCB;
143     prompt_result_ = rc;
144     listener_state_ = ListenerState::Terminating;
145     lock.unlock();
146     if (do_callback) {
147         auto error = resultCB->result(prompt_result_, msg, token);
148         if (!error.isOk()) {
149             if (error.getExceptionCode() == EX_SERVICE_SPECIFIC) {
150                 ConfUiLog(ERROR) << "Result callback failed error: "
151                                  << error.getServiceSpecificError();
152             } else {
153                 ConfUiLog(ERROR) << "Result callback failed error: " << error.getStatus();
154             }
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 shared_ptr<IConfirmationResultCallback> & resultCB,const vector<uint8_t> & promptTextBytes,const vector<uint8_t> & extraData,const string & locale,const vector<UIOption> & uiOptions)164 ::ndk::ScopedAStatus TrustyConfirmationUI::promptUserConfirmation(
165     const shared_ptr<IConfirmationResultCallback>& resultCB, const vector<uint8_t>& promptTextBytes,
166     const vector<uint8_t>& extraData, const string& locale, const vector<UIOption>& uiOptions) {
167     std::unique_lock<std::mutex> stateLock(listener_state_lock_, std::defer_lock);
168     ConfUiLog(INFO) << "promptUserConfirmation is called";
169     string promptText(promptTextBytes.begin(), promptTextBytes.end());
170     if (!is_supported_vm_) {
171         return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNIMPLEMENTED));
172     }
173     if (!stateLock.try_lock()) {
174         return ndk::ScopedAStatus(
175             AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING));
176     }
177     switch (listener_state_) {
178     case ListenerState::None:
179         break;
180     case ListenerState::Starting:
181     case ListenerState::SetupDone:
182     case ListenerState::Interactive:
183         return ndk::ScopedAStatus(
184             AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING));
185     case ListenerState::Terminating:
186         callback_thread_.join();
187         listener_state_ = ListenerState::None;
188         break;
189     default:
190         return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNEXPECTED));
191     }
192     assert(listener_state_ == ListenerState::None);
193     listener_state_ = ListenerState::Starting;
194 
195     current_session_id_++;
196     auto worker = [this](const shared_ptr<IConfirmationResultCallback>& resultCB,
197                          const string& promptText, const vector<uint8_t>& extraData,
198                          const string& locale, const vector<UIOption>& uiOptions) {
199         RunSession(resultCB, promptText, extraData, locale, uiOptions);
200     };
201     callback_thread_ = std::thread(worker, resultCB, promptText, extraData, locale, uiOptions);
202 
203     listener_state_condv_.wait(stateLock, [this] {
204         return listener_state_ == ListenerState::SetupDone ||
205                listener_state_ == ListenerState::Interactive ||
206                listener_state_ == ListenerState::Terminating;
207     });
208     if (listener_state_ == ListenerState::Terminating) {
209         callback_thread_.join();
210         listener_state_ = ListenerState::None;
211         if (prompt_result_ == IConfirmationUI::CANCELED) {
212             // VTS expects this
213             return ndk::ScopedAStatus::ok();
214         }
215         return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(prompt_result_));
216     }
217     return ndk::ScopedAStatus::ok();
218 }
219 
220 ::ndk::ScopedAStatus
deliverSecureInputEvent(const HardwareAuthToken & auth_token)221 TrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& auth_token) {
222     ConfUiLog(INFO) << "deliverSecureInputEvent is called";
223     int rc = IConfirmationUI::IGNORED;
224     if (!is_supported_vm_) {
225         return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNIMPLEMENTED));
226     }
227     {
228         std::unique_lock<std::mutex> lock(current_session_lock_);
229         if (!current_session_) {
230             return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(rc));
231         }
232         rc = current_session_->DeliverSecureInputEvent(auth_token);
233         if (rc != IConfirmationUI::OK) {
234             return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(rc));
235         }
236     }
237     return ndk::ScopedAStatus::ok();
238 }
239 
abort()240 ::ndk::ScopedAStatus TrustyConfirmationUI::abort() {
241     if (!is_supported_vm_) {
242         return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNIMPLEMENTED));
243     }
244     std::unique_lock<std::mutex> lock(current_session_lock_);
245     if (!current_session_) {
246         return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::IGNORED));
247     }
248     current_session_->Abort();
249     return ndk::ScopedAStatus::ok();
250 }
251 
252 }  // namespace aidl::android::hardware::confirmationui
253