/* * 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(); finalizeTransaction(ConfirmationResponseCode::Aborted, {}, true); *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OK); return Status::ok(); } // 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, bool callAbortOnHal) { // Note that confirmationUI->abort() may make the remote HAL process do an IPC call back // into our process resulting in confirmationResultCallback() to be called... this in turn // calls finalizeTransaction(). So we have to be careful a) not holding any locks; // and b) ensure state has been cleared; before doing this... mMutex.lock(); mRateLimiting.processResult(responseCode); sp<IBinder> listener = mCurrentListener; if (mCurrentListener != nullptr) { mCurrentListener->unlinkToDeath(mDeathRecipient); mCurrentListener = nullptr; } sp<IConfirmationUI> confirmationUI = mCurrentConfirmationUI; if (mCurrentConfirmationUI != nullptr) { mCurrentConfirmationUI->unlinkToDeath(this); mCurrentConfirmationUI = nullptr; } mMutex.unlock(); // Tell the HAL to shut down the confirmation dialog, if requested. if (confirmationUI != nullptr && callAbortOnHal) { confirmationUI->abort(); } // 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, false); 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"); finalizeTransaction(ConfirmationResponseCode::SystemError, {}, true); } 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, {}, false); } } // namespace keystore