/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "apc_compat.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #define LOG_TAG "keystore2_apc_compat" namespace keystore2 { using android::sp; using android::hardware::hidl_death_recipient; using android::hardware::hidl_vec; using android::hardware::Return; using android::hardware::Status; using HidlConfirmationResultCb = android::hardware::confirmationui::V1_0::IConfirmationResultCallback; using HidlConfirmationUI = android::hardware::confirmationui::V1_0::IConfirmationUI; using android::hardware::confirmationui::V1_0::ResponseCode; using HidlUIOptions = android::hardware::confirmationui::V1_0::UIOption; using AidlConfirmationUI = ::aidl::android::hardware::confirmationui::IConfirmationUI; using AidlBnConfirmationResultCb = ::aidl::android::hardware::confirmationui::BnConfirmationResultCallback; using AidlUIOptions = ::aidl::android::hardware::confirmationui::UIOption; class CompatSessionCB { public: void finalize(uint32_t responseCode, ApcCompatCallback callback, std::optional>> dataConfirmed, std::optional>> confirmationToken) { if (callback.result != nullptr) { size_t dataConfirmedSize = 0; const uint8_t* dataConfirmedPtr = nullptr; size_t confirmationTokenSize = 0; const uint8_t* confirmationTokenPtr = nullptr; if (responseCode == APC_COMPAT_ERROR_OK) { if (dataConfirmed) { dataConfirmedPtr = dataConfirmed->get().data(); dataConfirmedSize = dataConfirmed->get().size(); } if (confirmationToken) { confirmationTokenPtr = confirmationToken->get().data(); confirmationTokenSize = confirmationToken->get().size(); } } callback.result(callback.data, responseCode, dataConfirmedPtr, dataConfirmedSize, confirmationTokenPtr, confirmationTokenSize); } } }; class ConfuiHidlCompatSession : public HidlConfirmationResultCb, public hidl_death_recipient, public CompatSessionCB { public: static sp tryGetService() { sp service = HidlConfirmationUI::tryGetService(); if (service) { return sp(new ConfuiHidlCompatSession(std::move(service))); } else { return nullptr; } } uint32_t promptUserConfirmation(ApcCompatCallback callback, const char* prompt_text, const uint8_t* extra_data, size_t extra_data_size, const char* locale, ApcCompatUiOptions ui_options) { std::string hidl_prompt(prompt_text); std::vector hidl_extra(extra_data, extra_data + extra_data_size); std::vector hidl_ui_options; if (ui_options.inverted) { hidl_ui_options.push_back(HidlUIOptions::AccessibilityInverted); } if (ui_options.magnified) { hidl_ui_options.push_back(HidlUIOptions::AccessibilityMagnified); } auto lock = std::lock_guard(callback_lock_); if (callback_.result != nullptr) { return APC_COMPAT_ERROR_OPERATION_PENDING; } auto err = service_->linkToDeath(sp(this), 0); if (!err.isOk()) { LOG(ERROR) << "Communication error: promptUserConfirmation: " "Trying to register death recipient: " << err.description(); return APC_COMPAT_ERROR_SYSTEM_ERROR; } auto rc = service_->promptUserConfirmation(sp(this), hidl_prompt, hidl_extra, locale, hidl_ui_options); if (!rc.isOk()) { LOG(ERROR) << "Communication error: promptUserConfirmation: " << rc.description(); } else if (rc == ResponseCode::OK) { callback_ = callback; } return responseCode2Compat(rc.withDefault(ResponseCode::SystemError)); } void abort() { service_->abort(); } void finalize(ResponseCode responseCode, const hidl_vec& dataConfirmed, const hidl_vec& confirmationToken) { ApcCompatCallback callback; { auto lock = std::lock_guard(callback_lock_); // Calling the callback consumes the callback data structure. We have to make // sure that it can only be called once. callback = callback_; callback_ = {nullptr, nullptr}; // Unlock the callback_lock_ here. It must never be held while calling the callback. } if (callback.result != nullptr) { service_->unlinkToDeath(sp(this)); std::vector data = dataConfirmed; std::vector token = confirmationToken; CompatSessionCB::finalize(responseCode2Compat(responseCode), callback, data, token); } } // HidlConfirmationResultCb overrides: android::hardware::Return result(ResponseCode responseCode, const hidl_vec& dataConfirmed, const hidl_vec& confirmationToken) override { finalize(responseCode, dataConfirmed, confirmationToken); return Status::ok(); }; void serviceDied(uint64_t /* cookie */, const ::android::wp<::android::hidl::base::V1_0::IBase>& /* who */) override { finalize(ResponseCode::SystemError, {}, {}); } static uint32_t responseCode2Compat(ResponseCode rc) { switch (rc) { case ResponseCode::OK: return APC_COMPAT_ERROR_OK; case ResponseCode::Canceled: return APC_COMPAT_ERROR_CANCELLED; case ResponseCode::Aborted: return APC_COMPAT_ERROR_ABORTED; case ResponseCode::OperationPending: return APC_COMPAT_ERROR_OPERATION_PENDING; case ResponseCode::Ignored: return APC_COMPAT_ERROR_IGNORED; case ResponseCode::SystemError: case ResponseCode::Unimplemented: case ResponseCode::Unexpected: case ResponseCode::UIError: case ResponseCode::UIErrorMissingGlyph: case ResponseCode::UIErrorMessageTooLong: case ResponseCode::UIErrorMalformedUTF8Encoding: default: return APC_COMPAT_ERROR_SYSTEM_ERROR; } } private: ConfuiHidlCompatSession(sp service) : service_(service), callback_{nullptr, nullptr} {} sp service_; // The callback_lock_ protects the callback_ field against concurrent modification. // IMPORTANT: It must never be held while calling the call back. std::mutex callback_lock_; ApcCompatCallback callback_; }; class ConfuiAidlCompatSession : public AidlBnConfirmationResultCb, public CompatSessionCB { public: static std::shared_ptr tryGetService() { constexpr const char confirmationUIServiceName[] = "android.hardware.confirmationui.IConfirmationUI/default"; if (!AServiceManager_isDeclared(confirmationUIServiceName)) { LOG(INFO) << confirmationUIServiceName << " is not declared in VINTF"; return nullptr; } std::shared_ptr aidlService = AidlConfirmationUI::fromBinder( ndk::SpAIBinder(AServiceManager_waitForService(confirmationUIServiceName))); if (aidlService) { return ::ndk::SharedRefBase::make(aidlService); } return nullptr; } uint32_t promptUserConfirmation(ApcCompatCallback callback, const char* prompt_text, const uint8_t* extra_data, size_t extra_data_size, const char* locale, ApcCompatUiOptions ui_options) { std::vector aidl_prompt(prompt_text, prompt_text + strlen(prompt_text)); std::vector aidl_extra(extra_data, extra_data + extra_data_size); std::vector aidl_ui_options; if (ui_options.inverted) { aidl_ui_options.push_back(AidlUIOptions::ACCESSIBILITY_INVERTED); } if (ui_options.magnified) { aidl_ui_options.push_back(AidlUIOptions::ACCESSIBILITY_MAGNIFIED); } auto lock = std::lock_guard(callback_lock_); if (callback_.result != nullptr) { return APC_COMPAT_ERROR_OPERATION_PENDING; } if (!aidlService_) { return APC_COMPAT_ERROR_SYSTEM_ERROR; } auto linkRet = AIBinder_linkToDeath(aidlService_->asBinder().get(), death_recipient_.get(), this); if (linkRet != STATUS_OK) { LOG(ERROR) << "Communication error: promptUserConfirmation: " "Trying to register death recipient: "; return APC_COMPAT_ERROR_SYSTEM_ERROR; } auto rc = aidlService_->promptUserConfirmation(ref(), aidl_prompt, aidl_extra, locale, aidl_ui_options); int ret = getReturnCode(rc); if (ret == AidlConfirmationUI::OK) { callback_ = callback; } else { LOG(ERROR) << "Communication error: promptUserConfirmation: " << rc.getDescription(); } return responseCode2Compat(ret); } void abort() { if (aidlService_) { aidlService_->abort(); } } void finalize(int32_t responseCode, std::optional>> dataConfirmed, std::optional>> confirmationToken) { ApcCompatCallback callback; { auto lock = std::lock_guard(callback_lock_); // Calling the callback consumes the callback data structure. We have to make // sure that it can only be called once. callback = callback_; callback_ = {nullptr, nullptr}; // Unlock the callback_lock_ here. It must never be held while calling the callback. } if (callback.result != nullptr) { if (aidlService_) { AIBinder_unlinkToDeath(aidlService_->asBinder().get(), death_recipient_.get(), this); } CompatSessionCB::finalize(responseCode2Compat(responseCode), callback, dataConfirmed, confirmationToken); } } // AidlBnConfirmationResultCb overrides: ::ndk::ScopedAStatus result(int32_t responseCode, const std::vector& dataConfirmed, const std::vector& confirmationToken) override { finalize(responseCode, dataConfirmed, confirmationToken); return ::ndk::ScopedAStatus::ok(); }; void serviceDied() { aidlService_.reset(); aidlService_ = nullptr; finalize(AidlConfirmationUI::SYSTEM_ERROR, {}, {}); } static void binderDiedCallbackAidl(void* ptr) { LOG(ERROR) << __func__ << " : ConfuiAidlCompatSession Service died."; auto aidlSession = static_cast(ptr); if (aidlSession == nullptr) { LOG(ERROR) << __func__ << ": Null ConfuiAidlCompatSession HAL died."; return; } aidlSession->serviceDied(); } int getReturnCode(const ::ndk::ScopedAStatus& result) { if (result.isOk()) return AidlConfirmationUI::OK; if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) { return static_cast(result.getServiceSpecificError()); } return result.getStatus(); } uint32_t responseCode2Compat(int32_t rc) { switch (rc) { case AidlConfirmationUI::OK: return APC_COMPAT_ERROR_OK; case AidlConfirmationUI::CANCELED: return APC_COMPAT_ERROR_CANCELLED; case AidlConfirmationUI::ABORTED: return APC_COMPAT_ERROR_ABORTED; case AidlConfirmationUI::OPERATION_PENDING: return APC_COMPAT_ERROR_OPERATION_PENDING; case AidlConfirmationUI::IGNORED: return APC_COMPAT_ERROR_IGNORED; case AidlConfirmationUI::SYSTEM_ERROR: case AidlConfirmationUI::UNIMPLEMENTED: case AidlConfirmationUI::UNEXPECTED: case AidlConfirmationUI::UI_ERROR: case AidlConfirmationUI::UI_ERROR_MISSING_GLYPH: case AidlConfirmationUI::UI_ERROR_MESSAGE_TOO_LONG: case AidlConfirmationUI::UI_ERROR_MALFORMED_UTF8ENCODING: default: return APC_COMPAT_ERROR_SYSTEM_ERROR; } } ConfuiAidlCompatSession(std::shared_ptr service) : aidlService_(service), callback_{nullptr, nullptr} { death_recipient_ = ::ndk::ScopedAIBinder_DeathRecipient( AIBinder_DeathRecipient_new(binderDiedCallbackAidl)); } virtual ~ConfuiAidlCompatSession() = default; ConfuiAidlCompatSession(const ConfuiAidlCompatSession&) = delete; ConfuiAidlCompatSession& operator=(const ConfuiAidlCompatSession&) = delete; private: std::shared_ptr aidlService_; // The callback_lock_ protects the callback_ field against concurrent modification. // IMPORTANT: It must never be held while calling the call back. std::mutex callback_lock_; ApcCompatCallback callback_; ::ndk::ScopedAIBinder_DeathRecipient death_recipient_; }; class ApcCompatSession { public: static ApcCompatServiceHandle getApcCompatSession() { auto aidlCompatSession = ConfuiAidlCompatSession::tryGetService(); if (aidlCompatSession) { return new ApcCompatSession(std::move(aidlCompatSession), nullptr); } sp hidlCompatSession = ConfuiHidlCompatSession::tryGetService(); if (hidlCompatSession) { return new ApcCompatSession(nullptr, std::move(hidlCompatSession)); } LOG(ERROR) << "ConfirmationUI: Not found Service"; return nullptr; } uint32_t promptUserConfirmation(ApcCompatCallback callback, const char* prompt_text, const uint8_t* extra_data, size_t extra_data_size, char const* locale, ApcCompatUiOptions ui_options) { if (aidlCompatSession_) { return aidlCompatSession_->promptUserConfirmation(callback, prompt_text, extra_data, extra_data_size, locale, ui_options); } else { return hidlCompatSession_->promptUserConfirmation(callback, prompt_text, extra_data, extra_data_size, locale, ui_options); } } void abortUserConfirmation() { if (aidlCompatSession_) { return aidlCompatSession_->abort(); } else { return hidlCompatSession_->abort(); } } void closeUserConfirmationService() { // Closing the handle implicitly aborts an ongoing sessions. // Note that a resulting callback is still safely conducted, because we only delete a // StrongPointer below. libhwbinder still owns another StrongPointer to this session. abortUserConfirmation(); } ApcCompatSession(std::shared_ptr aidlCompatSession, sp hidlCompatSession) : aidlCompatSession_(aidlCompatSession), hidlCompatSession_(hidlCompatSession) {} private: std::shared_ptr aidlCompatSession_; sp hidlCompatSession_; }; } // namespace keystore2 using namespace keystore2; ApcCompatServiceHandle tryGetUserConfirmationService() { return reinterpret_cast(ApcCompatSession::getApcCompatSession()); } uint32_t promptUserConfirmation(ApcCompatServiceHandle handle, ApcCompatCallback callback, const char* prompt_text, const uint8_t* extra_data, size_t extra_data_size, char const* locale, ApcCompatUiOptions ui_options) { auto session = reinterpret_cast(handle); return session->promptUserConfirmation(callback, prompt_text, extra_data, extra_data_size, locale, ui_options); } void abortUserConfirmation(ApcCompatServiceHandle handle) { auto session = reinterpret_cast(handle); session->abortUserConfirmation(); } void closeUserConfirmationService(ApcCompatServiceHandle handle) { auto session = reinterpret_cast(handle); session->closeUserConfirmationService(); delete reinterpret_cast(handle); } const ApcCompatServiceHandle INVALID_SERVICE_HANDLE = nullptr;