|  | /* | 
|  | * Copyright (C) 2017 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. | 
|  | */ | 
|  |  | 
|  | #define LOG_TAG "ConfirmationManager" | 
|  |  | 
|  | #include "confirmation_manager.h" | 
|  |  | 
|  | #include <android/hardware/confirmationui/1.0/IConfirmationResultCallback.h> | 
|  | #include <android/hardware/confirmationui/1.0/IConfirmationUI.h> | 
|  | #include <android/hardware/confirmationui/1.0/types.h> | 
|  | #include <android/security/BpConfirmationPromptCallback.h> | 
|  | #include <binder/BpBinder.h> | 
|  | #include <binder/IPCThreadState.h> | 
|  | #include <binder/Parcel.h> | 
|  |  | 
|  | #include "keystore_aidl_hidl_marshalling_utils.h" | 
|  |  | 
|  | namespace keystore { | 
|  |  | 
|  | using android::IBinder; | 
|  | using android::sp; | 
|  | using android::String16; | 
|  | using android::String8; | 
|  | using android::wp; | 
|  | using android::binder::Status; | 
|  | using android::hardware::hidl_vec; | 
|  | using android::hardware::Return; | 
|  | using android::hardware::confirmationui::V1_0::IConfirmationResultCallback; | 
|  | using android::hardware::confirmationui::V1_0::IConfirmationUI; | 
|  | using android::hardware::confirmationui::V1_0::UIOption; | 
|  |  | 
|  | using android::security::BpConfirmationPromptCallback; | 
|  | using std::lock_guard; | 
|  | using std::mutex; | 
|  | using std::vector; | 
|  |  | 
|  | ConfirmationManager::ConfirmationManager(IBinder::DeathRecipient* deathRecipient) | 
|  | : IConfirmationResultCallback(), mDeathRecipient(deathRecipient) {} | 
|  |  | 
|  | // Called by keystore main thread. | 
|  | Status ConfirmationManager::presentConfirmationPrompt(const sp<IBinder>& listener, | 
|  | const String16& promptText, | 
|  | const hidl_vec<uint8_t>& extraData, | 
|  | const String16& locale, int uiOptionsAsFlags, | 
|  | int32_t* aidl_return) { | 
|  | lock_guard<mutex> lock(mMutex); | 
|  |  | 
|  | if (mCurrentListener != nullptr) { | 
|  | *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OperationPending); | 
|  | return Status::ok(); | 
|  | } | 
|  |  | 
|  | sp<IConfirmationUI> confirmationUI = IConfirmationUI::tryGetService(); | 
|  | if (confirmationUI == nullptr) { | 
|  | ALOGW("Error getting confirmationUI service\n"); | 
|  | *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::Unimplemented); | 
|  | return Status::ok(); | 
|  | } | 
|  |  | 
|  | uid_t callingUid = android::IPCThreadState::self()->getCallingUid(); | 
|  | if (!mRateLimiting.tryPrompt(callingUid)) { | 
|  | *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::SystemError); | 
|  | return Status::ok(); | 
|  | } | 
|  |  | 
|  | String8 promptText8(promptText); | 
|  | String8 locale8(locale); | 
|  | vector<UIOption> uiOptionsVector; | 
|  | for (int n = 0; n < 32; n++) { | 
|  | if (uiOptionsAsFlags & (1 << n)) { | 
|  | uiOptionsVector.push_back(UIOption(n)); | 
|  | } | 
|  | } | 
|  | ConfirmationResponseCode responseCode; | 
|  | responseCode = confirmationUI->promptUserConfirmation(sp<IConfirmationResultCallback>(this), | 
|  | promptText8.string(), extraData, | 
|  | locale8.string(), uiOptionsVector); | 
|  | if (responseCode != ConfirmationResponseCode::OK) { | 
|  | ALOGW("Unexpecxted responseCode %d from promptUserConfirmation\n", responseCode); | 
|  | *aidl_return = static_cast<int32_t>(responseCode); | 
|  | return Status::ok(); | 
|  | } | 
|  |  | 
|  | listener->linkToDeath(mDeathRecipient); | 
|  | confirmationUI->linkToDeath(this, 0); | 
|  | mCurrentListener = listener; | 
|  | mCurrentConfirmationUI = confirmationUI; | 
|  |  | 
|  | *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OK); | 
|  | return Status::ok(); | 
|  | } | 
|  |  | 
|  | // Called by keystore main thread. | 
|  | Status ConfirmationManager::cancelConfirmationPrompt(const sp<IBinder>& listener, | 
|  | int32_t* aidl_return) { | 
|  | mMutex.lock(); | 
|  | if (mCurrentListener != listener) { | 
|  | // If the prompt was displayed by another application, return | 
|  | // OperationPending. | 
|  | mMutex.unlock(); | 
|  | *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OperationPending); | 
|  | return Status::ok(); | 
|  | } | 
|  | mMutex.unlock(); | 
|  |  | 
|  | cancelPrompt(); | 
|  |  | 
|  | *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OK); | 
|  | return Status::ok(); | 
|  | } | 
|  |  | 
|  | void ConfirmationManager::cancelPrompt() { | 
|  | mMutex.lock(); | 
|  | mRateLimiting.cancelPrompt(); | 
|  | if (mCurrentListener != nullptr) { | 
|  | mCurrentListener->unlinkToDeath(mDeathRecipient); | 
|  | mCurrentListener = nullptr; | 
|  | } | 
|  | sp<IConfirmationUI> confirmationUI = mCurrentConfirmationUI; | 
|  | if (mCurrentConfirmationUI != nullptr) { | 
|  | mCurrentConfirmationUI->unlinkToDeath(this); | 
|  | mCurrentConfirmationUI = nullptr; | 
|  | } | 
|  | mMutex.unlock(); | 
|  | if (confirmationUI != nullptr) { | 
|  | confirmationUI->abort(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Called by keystore main thread. | 
|  | Status ConfirmationManager::isConfirmationPromptSupported(bool* aidl_return) { | 
|  | sp<IConfirmationUI> confirmationUI = IConfirmationUI::tryGetService(); | 
|  | if (confirmationUI == nullptr) { | 
|  | ALOGW("Error getting confirmationUI service\n"); | 
|  | *aidl_return = false; | 
|  | return Status::ok(); | 
|  | } | 
|  |  | 
|  | *aidl_return = true; | 
|  | return Status::ok(); | 
|  | } | 
|  |  | 
|  | void ConfirmationManager::finalizeTransaction(ConfirmationResponseCode responseCode, | 
|  | hidl_vec<uint8_t> dataThatWasConfirmed) { | 
|  | mMutex.lock(); | 
|  | mRateLimiting.processResult(responseCode); | 
|  | sp<IBinder> listener = mCurrentListener; | 
|  | if (mCurrentListener != nullptr) { | 
|  | mCurrentListener->unlinkToDeath(mDeathRecipient); | 
|  | mCurrentListener = nullptr; | 
|  | } | 
|  | if (mCurrentConfirmationUI != nullptr) { | 
|  | mCurrentConfirmationUI->unlinkToDeath(this); | 
|  | mCurrentConfirmationUI = nullptr; | 
|  | } | 
|  | mMutex.unlock(); | 
|  |  | 
|  | // Deliver result to the application that started the operation. | 
|  | if (listener != nullptr) { | 
|  | sp<BpConfirmationPromptCallback> obj = new BpConfirmationPromptCallback(listener); | 
|  | Status status = obj->onConfirmationPromptCompleted(static_cast<int32_t>(responseCode), | 
|  | dataThatWasConfirmed); | 
|  | if (!status.isOk()) { | 
|  | ALOGW("Error sending onConfirmationPromptCompleted - status: %s\n", | 
|  | status.toString8().c_str()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Called by hwbinder thread (not keystore main thread). | 
|  | Return<void> ConfirmationManager::result(ConfirmationResponseCode responseCode, | 
|  | const hidl_vec<uint8_t>& dataThatWasConfirmed, | 
|  | const hidl_vec<uint8_t>& confirmationToken) { | 
|  | finalizeTransaction(responseCode, dataThatWasConfirmed); | 
|  | lock_guard<mutex> lock(mMutex); | 
|  | mLatestConfirmationToken = confirmationToken; | 
|  | return Return<void>(); | 
|  | } | 
|  |  | 
|  | // Called by keystore main thread or keymaster worker | 
|  | hidl_vec<uint8_t> ConfirmationManager::getLatestConfirmationToken() { | 
|  | lock_guard<mutex> lock(mMutex); | 
|  | return mLatestConfirmationToken; | 
|  | } | 
|  |  | 
|  | void ConfirmationManager::binderDied(const wp<IBinder>& who) { | 
|  | // This is also called for other binders so need to check it's for | 
|  | // us before acting on it. | 
|  | mMutex.lock(); | 
|  | if (who == mCurrentListener) { | 
|  | // Clear this so we don't call back into the already dead | 
|  | // binder in finalizeTransaction(). | 
|  | mCurrentListener->unlinkToDeath(mDeathRecipient); | 
|  | mCurrentListener = nullptr; | 
|  | mMutex.unlock(); | 
|  | ALOGW("The process which requested the confirmation dialog died.\n"); | 
|  | cancelPrompt(); | 
|  | } else { | 
|  | mMutex.unlock(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ConfirmationManager::serviceDied(uint64_t /* cookie */, | 
|  | const wp<android::hidl::base::V1_0::IBase>& /* who */) { | 
|  | ALOGW("The ConfirmationUI HAL died.\n"); | 
|  | finalizeTransaction(ConfirmationResponseCode::SystemError, {}); | 
|  | } | 
|  |  | 
|  | }  // namespace keystore |