| /* |
| * 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 |