/*
 *
 * Copyright 2019, 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 "TrustyConfirmationUI.h"

#include <android-base/logging.h>
#include <fcntl.h>
#include <linux/input.h>
#include <poll.h>
#include <pthread.h>
#include <secure_input/evdev.h>
#include <secure_input/secure_input_device.h>
#include <secure_input/secure_input_proto.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <teeui/msg_formatting.h>
#include <teeui/utils.h>
#include <time.h>

#include <atomic>
#include <functional>
#include <memory>
#include <thread>
#include <tuple>
#include <vector>

namespace aidl::android::hardware::confirmationui {
using namespace secure_input;

using ::android::trusty::confirmationui::TrustyAppError;

using ::teeui::AbortMsg;
using ::teeui::DeliverTestCommandMessage;
using ::teeui::DeliverTestCommandResponse;
using ::teeui::FetchConfirmationResult;
using ::teeui::MsgString;
using ::teeui::MsgVector;
using ::teeui::PromptUserConfirmationMsg;
using ::teeui::PromptUserConfirmationResponse;
using ::teeui::ResultMsg;

using ::secure_input::createSecureInput;

using ::std::tie;

using TeeuiRc = ::teeui::ResponseCode;

constexpr const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0";
constexpr const char kConfirmationuiAppName[] = CONFIRMATIONUI_PORT;

namespace {

class Finalize {
  private:
    std::function<void()> f_;

  public:
    Finalize(std::function<void()> f) : f_(f) {}
    ~Finalize() {
        if (f_) f_();
    }
    void release() { f_ = {}; }
};

int convertRc(TeeuiRc trc) {
    static_assert(
        uint32_t(TeeuiRc::OK) == uint32_t(IConfirmationUI::OK) &&
            uint32_t(TeeuiRc::Canceled) == uint32_t(IConfirmationUI::CANCELED) &&
            uint32_t(TeeuiRc::Aborted) == uint32_t(IConfirmationUI::ABORTED) &&
            uint32_t(TeeuiRc::OperationPending) == uint32_t(IConfirmationUI::OPERATION_PENDING) &&
            uint32_t(TeeuiRc::Ignored) == uint32_t(IConfirmationUI::IGNORED) &&
            uint32_t(TeeuiRc::SystemError) == uint32_t(IConfirmationUI::SYSTEM_ERROR) &&
            uint32_t(TeeuiRc::Unimplemented) == uint32_t(IConfirmationUI::UNIMPLEMENTED) &&
            uint32_t(TeeuiRc::Unexpected) == uint32_t(IConfirmationUI::UNEXPECTED) &&
            uint32_t(TeeuiRc::UIError) == uint32_t(IConfirmationUI::UI_ERROR) &&
            uint32_t(TeeuiRc::UIErrorMissingGlyph) ==
                uint32_t(IConfirmationUI::UI_ERROR_MISSING_GLYPH) &&
            uint32_t(TeeuiRc::UIErrorMessageTooLong) ==
                uint32_t(IConfirmationUI::UI_ERROR_MESSAGE_TOO_LONG) &&
            uint32_t(TeeuiRc::UIErrorMalformedUTF8Encoding) ==
                uint32_t(IConfirmationUI::UI_ERROR_MALFORMED_UTF8ENCODING),
        "teeui::ResponseCode and "
        "::android::hardware::confirmationui::V1_0::Responsecude are out of "
        "sync");
    return static_cast<int>(trc);
}

teeui::UIOption convertUIOption(UIOption uio) {
    static_assert(uint32_t(UIOption::ACCESSIBILITY_INVERTED) ==
                          uint32_t(teeui::UIOption::AccessibilityInverted) &&
                      uint32_t(UIOption::ACCESSIBILITY_MAGNIFIED) ==
                          uint32_t(teeui::UIOption::AccessibilityMagnified),
                  "teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption "
                  "are out of sync");
    return teeui::UIOption(uio);
}

inline MsgString stdString2MsgString(const string& s) {
    return {s.c_str(), s.c_str() + s.size()};
}
template <typename T> inline MsgVector<T> stdVector2MsgVector(const vector<T>& v) {
    return {v};
}

inline MsgVector<teeui::UIOption> stdVector2MsgVector(const vector<UIOption>& v) {
    MsgVector<teeui::UIOption> result(v.size());
    for (unsigned int i = 0; i < v.size(); ++i) {
        result[i] = convertUIOption(v[i]);
    }
    return result;
}

}  // namespace

TrustyConfirmationUI::TrustyConfirmationUI()
    : listener_state_(ListenerState::None), prompt_result_(IConfirmationUI::IGNORED) {}

TrustyConfirmationUI::~TrustyConfirmationUI() {
    ListenerState state = listener_state_;
    if (state == ListenerState::SetupDone || state == ListenerState::Interactive) {
        abort();
    }
    if (state != ListenerState::None) {
        callback_thread_.join();
    }
}

std::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>>
TrustyConfirmationUI::promptUserConfirmation_(const MsgString& promptText,
                                              const MsgVector<uint8_t>& extraData,
                                              const MsgString& locale,
                                              const MsgVector<teeui::UIOption>& uiOptions) {
    std::unique_lock<std::mutex> stateLock(listener_state_lock_);
    /*
     * This is the main listener thread function. The listener thread life cycle
     * is equivalent to the life cycle of a single confirmation request. The life
     * cycle is devided in four phases.
     *  * The starting phase:
     *    * The Trusted App gets loaded and/or the connection to it gets established.
     *    * A connection to the secure input device is established.
     *    * The prompt is initiated. This sends all information required by the
     *      confirmation dialog to the TA. The dialog is not yet displayed.
     *    * An event loop is created.
     *      * The event loop listens for user input events, fetches them from the
     *        secure input device, and delivers them to the TA.
     *    * All evdev devices are grabbed to give confirmationui exclusive access
     *      to user input.
     *
     * Note: During the starting phase the hwbinder service thread is blocked and
     * waiting for possible Errors. If the setup phase concludes sucessfully, the
     * hwbinder service thread gets unblocked and returns successfully. Errors
     * that occur after the first phase are delivered by callback interface.
     *
     *  * The 2nd phase - non interactive phase
     *    * The event loop thread is started.
     *    * After a grace period:
     *      * A handshake between the secure input device SecureInput and the TA
     *        is performed.
     *      * The input event handler are armed to process user input events.
     *
     *  * The 3rd phase - interactive phase
     *    * We wait to any external event
     *      * Abort
     *      * Secure user input asserted
     *      * Secure input delivered (for non interactive VTS testing)
     *    * The result is fetched from the TA.
     *
     *  * The 4th phase - cleanup
     *    The cleanup phase is given by the scope of automatic variables created
     *    in this function. The cleanup commences in reverse order of their creation.
     *    Here is a list of more complex items in the order in which they go out of
     *    scope
     *    * finalizeSecureTouch - signals and joins the secure touch thread.
     *    * eventloop - signals and joins the event loop thread. The event
     *      handlers also own all EventDev instances which ungrab the event devices.
     *      When the eventloop goes out of scope the EventDevs get destroyed
     *      relinquishing the exclusive hold on the event devices.
     *    * finalizeConfirmationPrompt - calls abort on the TA, making sure a
     *      pending operation gets canceled. If the prompt concluded successfully this
     *      is a spurious call but semantically a no op.
     *    * secureInput - shuts down the connection to the secure input device
     *      SecureInput.
     *    * app - disconnects the TA. Since app is a shared pointer this may not
     *      unload the app here. It is possible that more instances of the shared
     *      pointer are held in TrustyConfirmationUI::deliverSecureInputEvent and
     *      TrustyConfirmationUI::abort. But these instances are extremely short lived
     *      and it is safe if they are destroyed by either.
     *    * stateLock - unlocks the listener_state_lock_ if it happens to be held
     *      at the time of return.
     */

    std::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>> result;
    TeeuiRc& rc = std::get<TeeuiRc>(result);
    rc = TeeuiRc::SystemError;

    listener_state_ = ListenerState::Starting;

    auto app = std::make_shared<TrustyApp>(kTrustyDeviceName, kConfirmationuiAppName);
    if (!app) return result;  // TeeuiRc::SystemError

    app_ = app;

    auto hsBegin = [&]() -> std::tuple<TeeuiRc, Nonce> {
        auto [error, result] =
            app->issueCmd<secure_input::InputHandshake, secure_input::InputHandshakeResponse>();
        auto& [rc, nCo] = result;

        if (error != TrustyAppError::OK || rc != TeeuiRc::OK) {
            LOG(ERROR) << "Failed to begin secure input handshake (" << int32_t(error) << "/"
                       << uint32_t(rc) << ")";
            rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc;
        }
        return result;
    };

    auto hsFinalize = [&](const Signature& sig, const Nonce& nCi) -> TeeuiRc {
        auto [error, finalizeResponse] =
            app->issueCmd<FinalizeInputSessionHandshake, FinalizeInputSessionHandshakeResponse>(
                nCi, sig);
        auto& [rc] = finalizeResponse;
        if (error != TrustyAppError::OK || rc != TeeuiRc::OK) {
            LOG(ERROR) << "Failed to finalize secure input handshake (" << int32_t(error) << "/"
                       << uint32_t(rc) << ")";
            rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc;
        }
        return rc;
    };

    auto deliverInput = [&](DTupKeyEvent event,
                            const Signature& sig) -> std::tuple<TeeuiRc, InputResponse> {
        auto [error, result] =
            app->issueCmd<DeliverInputEvent, DeliverInputEventResponse>(event, sig);
        auto& [rc, ir] = result;
        if (error != TrustyAppError::OK) {
            LOG(ERROR) << "Failed to deliver input command";
            rc = TeeuiRc::SystemError;
        }
        return result;
    };

    std::atomic<TeeuiRc> eventRC = TeeuiRc::OperationPending;
    auto inputResult = [&](TeeuiRc rc) {
        TeeuiRc expected = TeeuiRc::OperationPending;
        if (eventRC.compare_exchange_strong(expected, rc)) {
            listener_state_condv_.notify_all();
        }
    };

    // create Secure Input device.
    auto secureInput = createSecureInput(hsBegin, hsFinalize, deliverInput, inputResult);
    if (!secureInput || !(*secureInput)) {
        LOG(ERROR) << "Failed to open secure input device";
        return result;  // TeeuiRc::SystemError;
    }

    Finalize finalizeConfirmationPrompt([app] {
        LOG(INFO) << "Calling abort for cleanup";
        app->issueCmd<AbortMsg>();
    });

    // initiate prompt
    LOG(INFO) << "Initiating prompt";
    TrustyAppError error;
    auto initResponse = std::tie(rc);
    std::tie(error, initResponse) =
        app->issueCmd<PromptUserConfirmationMsg, PromptUserConfirmationResponse>(
            promptText, extraData, locale, uiOptions);
    if (error == TrustyAppError::MSG_TOO_LONG) {
        LOG(ERROR) << "PromptUserConfirmationMsg failed: message too long";
        rc = TeeuiRc::UIErrorMessageTooLong;
        return result;
    } else if (error != TrustyAppError::OK) {
        LOG(ERROR) << "PromptUserConfirmationMsg failed: " << int32_t(error);
        return result;  // TeeuiRc::SystemError;
    }
    if (rc != TeeuiRc::OK) {
        LOG(ERROR) << "PromptUserConfirmationMsg failed: " << uint32_t(rc);
        return result;
    }

    LOG(INFO) << "Grabbing event devices";
    EventLoop eventloop;
    bool grabbed =
        grabAllEvDevsAndRegisterCallbacks(&eventloop, [&](short flags, const EventDev& evDev) {
            if (!(flags & POLLIN)) return;
            secureInput->handleEvent(evDev);
        });

    if (!grabbed) {
        rc = TeeuiRc::SystemError;
        return result;
    }

    abort_called_ = false;
    secureInputDelivered_ = false;

    //  ############################## Start 2nd Phase #############################################
    listener_state_ = ListenerState::SetupDone;
    stateLock.unlock();
    listener_state_condv_.notify_all();

    if (!eventloop.start()) {
        rc = TeeuiRc::SystemError;
        return result;
    }

    stateLock.lock();

    LOG(INFO) << "going to sleep for the grace period";
    auto then = std::chrono::system_clock::now() +
                std::chrono::milliseconds(kUserPreInputGracePeriodMillis) +
                std::chrono::microseconds(50);
    listener_state_condv_.wait_until(stateLock, then, [&]() { return abort_called_; });
    LOG(INFO) << "waking up";

    if (abort_called_) {
        LOG(ERROR) << "Abort called";
        result = {TeeuiRc::Aborted, {}, {}};
        return result;
    }

    LOG(INFO) << "Arming event poller";
    // tell the event poller to act on received input events from now on.
    secureInput->start();

    //  ############################## Start 3rd Phase - interactive phase #########################
    LOG(INFO) << "Transition to Interactive";
    listener_state_ = ListenerState::Interactive;
    stateLock.unlock();
    listener_state_condv_.notify_all();

    stateLock.lock();
    listener_state_condv_.wait(stateLock, [&]() {
        return eventRC != TeeuiRc::OperationPending || abort_called_ || secureInputDelivered_;
    });
    LOG(INFO) << "Listener waking up";
    if (abort_called_) {
        LOG(ERROR) << "Abort called";
        result = {TeeuiRc::Aborted, {}, {}};
        return result;
    }

    if (!secureInputDelivered_) {
        if (eventRC != TeeuiRc::OK) {
            LOG(ERROR) << "Bad input response";
            result = {eventRC, {}, {}};
            return result;
        }
    }

    stateLock.unlock();

    LOG(INFO) << "Fetching Result";
    std::tie(error, result) = app->issueCmd<FetchConfirmationResult, ResultMsg>();
    LOG(INFO) << "Result yields " << int32_t(error) << "/" << uint32_t(rc);
    if (error != TrustyAppError::OK) {
        result = {TeeuiRc::SystemError, {}, {}};
    }
    return result;

    //  ############################## Start 4th Phase - cleanup ##################################
}

// Methods from ::aidl::android::hardware::confirmationui::IConfirmationUI
// follow.
::ndk::ScopedAStatus TrustyConfirmationUI::promptUserConfirmation(
    const shared_ptr<IConfirmationResultCallback>& resultCB, const vector<uint8_t>& promptTextBytes,
    const vector<uint8_t>& extraData, const string& locale, const vector<UIOption>& uiOptions) {
    std::unique_lock<std::mutex> stateLock(listener_state_lock_, std::defer_lock);
    string promptText(promptTextBytes.begin(), promptTextBytes.end());
    if (!stateLock.try_lock()) {
        return ndk::ScopedAStatus(
            AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING));
    }
    switch (listener_state_) {
    case ListenerState::None:
        break;
    case ListenerState::Starting:
    case ListenerState::SetupDone:
    case ListenerState::Interactive:
        return ndk::ScopedAStatus(
            AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING));
    case ListenerState::Terminating:
        callback_thread_.join();
        listener_state_ = ListenerState::None;
        break;
    default:
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNEXPECTED));
    }

    assert(listener_state_ == ListenerState::None);

    callback_thread_ = std::thread(
        [this](const shared_ptr<IConfirmationResultCallback>& resultCB, const string& promptText,
               const vector<uint8_t>& extraData, const string& locale,
               const vector<UIOption>& uiOptions) {
            auto [trc, msg, token] = promptUserConfirmation_(
                stdString2MsgString(promptText), stdVector2MsgVector(extraData),
                stdString2MsgString(locale), stdVector2MsgVector(uiOptions));
            bool do_callback = (listener_state_ == ListenerState::Interactive ||
                                listener_state_ == ListenerState::SetupDone) &&
                               resultCB;
            prompt_result_ = convertRc(trc);
            listener_state_ = ListenerState::Terminating;
            if (do_callback) {
                auto error = resultCB->result(prompt_result_, msg, token);
                if (!error.isOk()) {
                    LOG(ERROR) << "Result callback failed " << error.getDescription();
                }
            } else {
                listener_state_condv_.notify_all();
            }
        },
        resultCB, promptText, extraData, locale, uiOptions);

    listener_state_condv_.wait(stateLock, [this] {
        return listener_state_ == ListenerState::SetupDone ||
               listener_state_ == ListenerState::Interactive ||
               listener_state_ == ListenerState::Terminating;
    });
    if (listener_state_ == ListenerState::Terminating) {
        callback_thread_.join();
        listener_state_ = ListenerState::None;
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(prompt_result_));
    }
    return ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus
TrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) {
    int rc = IConfirmationUI::IGNORED;
    {
        /*
         * deliverSecureInputEvent is only used by the VTS test to mock human input. A correct
         * implementation responds with a mock confirmation token signed with a test key. The
         * problem is that the non interactive grace period was not formalized in the HAL spec,
         * so that the VTS test does not account for the grace period. (It probably should.)
         * This means we can only pass the VTS test if we block until the grace period is over
         * (SetupDone -> Interactive) before we deliver the input event.
         *
         * The true secure input is delivered by a different mechanism and gets ignored -
         * not queued - until the grace period is over.
         *
         */
        std::unique_lock<std::mutex> stateLock(listener_state_lock_);
        listener_state_condv_.wait(stateLock,
                                   [this] { return listener_state_ != ListenerState::SetupDone; });

        if (listener_state_ != ListenerState::Interactive)
            return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::IGNORED));
        auto sapp = app_.lock();
        if (!sapp)
            return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::IGNORED));
        auto [error, response] =
            sapp->issueCmd<DeliverTestCommandMessage, DeliverTestCommandResponse>(
                static_cast<teeui::TestModeCommands>(secureInputToken.challenge));
        if (error != TrustyAppError::OK)
            return ndk::ScopedAStatus(
                AStatus_fromServiceSpecificError(IConfirmationUI::SYSTEM_ERROR));
        auto& [trc] = response;
        if (trc != TeeuiRc::Ignored) secureInputDelivered_ = true;
        rc = convertRc(trc);
    }
    if (secureInputDelivered_) listener_state_condv_.notify_all();
    // VTS test expect an OK response if the event was successfully delivered.
    // But since the TA returns the callback response now, we have to translate
    // Canceled into OK. Canceled is only returned if the delivered event canceled
    // the operation, which means that the event was successfully delivered. Thus
    // we return OK.
    if (rc == IConfirmationUI::CANCELED) return ndk::ScopedAStatus::ok();
    if (rc != IConfirmationUI::OK) {
        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(rc));
    }
    return ndk::ScopedAStatus::ok();
}

::ndk::ScopedAStatus TrustyConfirmationUI::abort() {
    {
        std::unique_lock<std::mutex> stateLock(listener_state_lock_);
        if (listener_state_ == ListenerState::SetupDone ||
            listener_state_ == ListenerState::Interactive) {
            auto sapp = app_.lock();
            if (sapp) sapp->issueCmd<AbortMsg>();
            abort_called_ = true;
        }
    }
    listener_state_condv_.notify_all();
    return ndk::ScopedAStatus::ok();
}

std::shared_ptr<IConfirmationUI> createTrustyConfirmationUI() {
    return ndk::SharedRefBase::make<TrustyConfirmationUI>();
}

}  // namespace aidl::android::hardware::confirmationui
