/*
 * Copyright (c) 2020 Huawei Device Co., Ltd.
 * 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 "ability_mgr_feature.h"

#include "ability_callback_utils.h"
#include "ability_connect_trans_param.h"
#include "ability_errors.h"
#include "ability_info.h"
#include "ability_message_id.h"
#include "ability_mgr_handler.h"
#include "ability_service_interface.h"
#include "adapter.h"
#include "ohos_init.h"
#include "iproxy_client.h"
#include "rpc_errno.h"
#include "samgr_lite.h"
#include "securec.h"
#include "util/abilityms_helper.h"
#include "utils.h"
#include "want_utils.h"

namespace OHOS {
SvcIdentity AbilityMgrFeature::svc_ = {0};
IDmsListener* AbilityMgrFeature::myCallback_ = nullptr;

AbilityMgrFeatureImpl g_amsImpl = {
    SERVER_IPROXY_IMPL_BEGIN,
    .Invoke = AbilityMgrFeature::Invoke,
    .StartAbility = AbilityMgrFeature::StartAbility,
    .TerminateAbility = AbilityMgrFeature::TerminateAbility,
    .ConnectAbility = AbilityMgrFeature::ConnectAbility,
    .DisconnectAbility = AbilityMgrFeature::DisconnectAbility,
    .StopAbility = AbilityMgrFeature::StopAbility,
    IPROXY_END
};

InvokeFunc AbilityMgrFeature::invokeFuncList[INNER_BEGIN] {
    AbilityMgrFeature::StartAbilityInvoke,
    AbilityMgrFeature::TerminateAbilityInvoke,
    AbilityMgrFeature::AttachBundleInvoke,
    AbilityMgrFeature::ConnectAbilityInvoke,
    AbilityMgrFeature::ConnectAbilityDoneInvoke,
    AbilityMgrFeature::DisconnectAbilityInvoke,
    AbilityMgrFeature::DisconnectAbilityDoneInvoke,
    AbilityMgrFeature::AbilityTransactionDoneInvoke,
    AbilityMgrFeature::StopAbilityInvoke,
    AbilityMgrFeature::StartAbilityWithCbInvoke,
};

static void Init()
{
    SamgrLite *samgrLite = SAMGR_GetInstance();
    CHECK_NULLPTR_RETURN(samgrLite, "AbilityMgrFeature", "get samgr error");
    BOOL result = samgrLite->RegisterFeature(AMS_SERVICE, AbilityMgrFeature::GetInstance());
    if (result == FALSE) {
        PRINTE("AbilityMgrFeature", "ams register feature failure");
        return;
    }
    g_amsImpl.ams = AbilityMgrFeature::GetInstance();
    auto publicApi = GET_IUNKNOWN(g_amsImpl);
    CHECK_NULLPTR_RETURN(publicApi, "AbilityMgrFeatureLite", "publicApi is nullptr");
    BOOL apiResult = samgrLite->RegisterFeatureApi(AMS_SERVICE, AMS_FEATURE, publicApi);
    PRINTI("AbilityMgrFeature", "ams feature init %{public}s", apiResult ? "success" : "failure");
}
SYSEX_FEATURE_INIT(Init);

AbilityMgrFeature::AbilityMgrFeature() : Feature(), identity_()
{
    this->Feature::GetName = AbilityMgrFeature::GetFeatureName;
    this->Feature::OnInitialize = AbilityMgrFeature::OnFeatureInitialize;
    this->Feature::OnStop = AbilityMgrFeature::OnFeatureStop;
    this->Feature::OnMessage = AbilityMgrFeature::OnFeatureMessage;
}

int32 AbilityMgrFeature::Invoke(IServerProxy *iProxy, int funcId, void *origin, IpcIo *req, IpcIo *reply)
{
    PRINTI("AbilityMgrFeature", "ams invoke called");
    if (req == nullptr) {
        return EC_INVALID;
    }
    if (funcId >= START_ABILITY && funcId < INNER_BEGIN) {
        return invokeFuncList[funcId](origin, req);
    }
    return COMMAND_ERROR;
}

const char *AbilityMgrFeature::GetFeatureName(Feature *feature)
{
    (void) feature;
    return AMS_FEATURE;
}

void AbilityMgrFeature::OnFeatureInitialize(Feature *feature, Service *parent, Identity identity)
{
    CHECK_NULLPTR_RETURN(feature, "AbilityMgrFeature", "initialize fail");
    (static_cast<AbilityMgrFeature *>(feature))->identity_ = identity;
    AbilityMgrHandler::GetInstance().Init();
}

void AbilityMgrFeature::OnFeatureStop(Feature *feature, Identity identity)
{
    (void) feature;
    (void) identity;
    AdapterFree(myCallback_);
}

BOOL AbilityMgrFeature::OnFeatureMessage(Feature *feature, Request *request)
{
    if (feature == nullptr || request == nullptr) {
        return FALSE;
    }

    AbilityMgrHandler::GetInstance().ServiceMsgProcess(*request);
    return TRUE;
}

void AbilityMgrFeature::OnRequestCallback(const void *data, int32_t ret)
{
    IpcIo io;
    char ipcData[MAX_IO_SIZE];
    IpcIo reply;
    IpcIoInit(&io, ipcData, MAX_IO_SIZE, 0);
    WriteInt32(&io, static_cast<int32_t>(ret));
    MessageOption option;
    MessageOptionInit(&option);
    option.flags = TF_OP_ASYNC;
    int32_t transRet = SendRequest(svc_, START_ABILITY_CALLBACK, &io, &reply, option, NULL);
    if (transRet != ERR_NONE) {
        HILOG_ERROR(HILOG_MODULE_APP, "AbilityMgrFeature InnerSelfTransact failed %{public}d\n", ret);
    }
    ReleaseSvc(svc_);
}

int32 AbilityMgrFeature::StartAbilityInvoke(const void *origin, IpcIo *req)
{
    pid_t uid = GetCallingUid();
    if (uid < 0) {
        PRINTE("AbilityMgrFeature", "invalid uid argument");
        return EC_INVALID;
    }
    Want want = { nullptr, nullptr, nullptr, 0};
    if (!DeserializeWant(&want, req)) {
        return EC_FAILURE;
    }
    if (want.element == nullptr) {
        PRINTE("AbilityMgrFeature", "invalid argument");
        return EC_INVALID;
    }
    const char *deviceId = want.element->deviceId;

    int32 retVal;
    if (deviceId != nullptr && *deviceId != '\0') {
        retVal = StartRemoteAbilityInner(&want, deviceId, uid, nullptr);
    } else {
        retVal = StartAbilityInner(&want, uid);
    }
    ClearWant(&want);
    return retVal;
}

int32 AbilityMgrFeature::StartAbilityWithCbInvoke(const void *origin, IpcIo *req)
{
    pid_t uid = GetCallingUid();
    if (uid < 0) {
        PRINTE("AbilityMgrFeature", "invalid uid argument");
        return EC_INVALID;
    }
    SvcIdentity svc;
    bool ret = ReadRemoteObject(req, &svc);
    if (!ret) {
        svc_ = {0};
        return EC_INVALID;
    }
    svc_ = svc;
    Want want = { nullptr, nullptr, nullptr, 0};
    if (!DeserializeWant(&want, req)) {
        return EC_FAILURE;
    }
    if (want.element == nullptr) {
        PRINTE("AbilityMgrFeature", "invalid argument");
        return EC_INVALID;
    }
    const char *deviceId = want.element->deviceId;

    int32 retVal = EC_FAILURE;
    if (deviceId != nullptr && *deviceId != '\0') {
        retVal = StartRemoteAbilityInner(&want, deviceId, uid, OnRequestCallback);
    }
    ClearWant(&want);
    return retVal;
}

int32 AbilityMgrFeature::StartAbility(const Want *want)
{
    return StartAbilityInner(want, -1);
}

int32 AbilityMgrFeature::StartRemoteAbilityInner(const Want *want, const char *deviceId,
    pid_t uid, OnRequestCallbackFunc callback)
{
    IUnknown *iUnknown = SAMGR_GetInstance()->GetFeatureApi(DISTRIBUTED_SCHEDULE_SERVICE, DMSLITE_FEATURE);
    DmsProxy *dmsInterface = NULL;
    if (iUnknown == NULL) {
        return EC_INVALID;
    }
    int32 retVal = iUnknown->QueryInterface(iUnknown, DEFAULT_VERSION, (void**) &dmsInterface);
    if (retVal != EC_SUCCESS) {
        return EC_INVALID;
    }
    CallerInfo callerInfo = {
        .uid = uid
    };

    if (callback != nullptr) {
        if (myCallback_ == nullptr) {
            myCallback_ = new IDmsListener();
        }
        myCallback_ -> OnResultCallback = callback;
        retVal = dmsInterface->StartRemoteAbility((Want *)want, &callerInfo, myCallback_);
    } else {
        retVal = dmsInterface->StartRemoteAbility((Want *)want, &callerInfo, NULL);
    }
    return retVal;
}

int32 AbilityMgrFeature::StartAbilityInner(const Want *want, pid_t callingUid)
{
    if (want == nullptr || want->element == nullptr) {
        PRINTE("AbilityMgrFeature", "invalid argument");
        return EC_INVALID;
    }
    Want *data = new Want();
    if (memset_s(data, sizeof(Want), 0, sizeof(Want)) != EOK) {
        PRINTE("AbilityMgrFeature", "memory alloc error");
        delete data;
        return EC_NOMEMORY;
    }
    SetWantElement(data, *(want->element));
    SetWantData(data, want->data, want->dataLength);
    if (want->sid != nullptr) {
        SetWantSvcIdentity(data, *(want->sid));
    }
    Request request = {
        .msgId = AMS_START_ABILITY,
        .len = 0,
        .data = reinterpret_cast<void *>(data),
        .msgValue = static_cast<uint32>(callingUid),
    };
    int32 propRet = SAMGR_SendRequest(&(GetInstance()->identity_), &request, nullptr);
    if (propRet != EC_SUCCESS) {
        PRINTE("AbilityMgrFeature", "send request failure");
        ClearWant(data);
        delete data;
        return EC_COMMU;
    }
    return EC_SUCCESS;
}

int32 AbilityMgrFeature::TerminateAbilityInvoke(const void *origin, IpcIo *req)
{
    uint64_t token = 0;
    ReadUint64(req, &token);
    return TerminateAbility(token);
}

int32 AbilityMgrFeature::TerminateAbility(uint64_t token)
{
    uint64_t *terminateToken = new uint64_t;
    *terminateToken = token;
    Request request = {
        .msgId = AMS_TERMINATE_ABILITY,
        .len = 0,
        .data = reinterpret_cast<void *>(terminateToken),
    };
    int32 propRet = SAMGR_SendRequest(&(GetInstance()->identity_), &request, nullptr);
    if (propRet != EC_SUCCESS) {
        PRINTE("AbilityMgrFeature", "send request failure");
        delete terminateToken;
        return EC_COMMU;
    }
    return EC_SUCCESS;
}

int32 AbilityMgrFeature::AbilityTransactionDoneInvoke(const void *origin, IpcIo *req)
{
    uint64_t token = 0;
    ReadUint64(req, &token);
    int32_t ret = 0;
    ReadInt32(req, &ret);
    int32 state = static_cast<int32>(ret);

    TransactionState *transactionState = new TransactionState();
    transactionState->token = token;
    transactionState->state = state;
    Request request = {
        .msgId = AMS_TRANSACTION_DONE,
        .len = 0,
        .data = reinterpret_cast<void *>(transactionState),
    };
    int32 propRet = SAMGR_SendRequest(&(GetInstance()->identity_), &request, nullptr);
    if (propRet != EC_SUCCESS) {
        PRINTE("AbilityMgrFeature", "send request failure");
        delete transactionState;
        return EC_COMMU;
    }
    return EC_SUCCESS;
}

int32 AbilityMgrFeature::AttachBundleInvoke(const void *origin, IpcIo *req)
{
    uint64_t token = 0;
    ReadUint64(req, &token);
    SvcIdentity svc;
    bool ret = ReadRemoteObject(req, &svc);
    if (!ret) {
        return EC_INVALID;
    }

#ifdef __LINUX__
    uint64_t readData = 0;
    ReadUint64(req, &readData);
    pid_t callingPid = static_cast<pid_t>(readData);
#else
    pid_t callingPid = GetCallingPid();
#endif
    if (callingPid < 0) {
        PRINTE("AbilityMgrFeature", "invalid pid argument");
        return EC_INVALID;
    }

    auto client = new AbilityThreadClient(token, callingPid, svc, &AbilityMgrFeature::AppDeathNotify);
    Request request = {
        .msgId = AMS_ATTACH_BUNDLE,
        .len = 0,
        .data = reinterpret_cast<void *>(client),
    };
    int32 propRet = SAMGR_SendRequest(&(GetInstance()->identity_), &request, nullptr);
    if (propRet != EC_SUCCESS) {
        PRINTE("AbilityMgrFeature", "send request failure");
        delete client;
        return EC_COMMU;
    }
    return EC_SUCCESS;
}

int32 AbilityMgrFeature::ConnectAbilityInvoke(const void *origin, IpcIo *req)
{
    pid_t uid = GetCallingUid();
    if (uid < 0) {
        PRINTE("AbilityMgrFeature", "invalid uid argument");
        return EC_INVALID;
    }
    uint64_t token = 0;
    ReadUint64(req, &token);
    SvcIdentity svc;
    bool ret = ReadRemoteObject(req, &svc);
    if (!ret) {
        return EC_INVALID;
    }
    Want want = { nullptr, nullptr, nullptr, 0 };
    if (!DeserializeWant(&want, req)) {
        return EC_FAILURE;
    }
    int32 retVal = ConnectAbilityInner(&want, &svc, token, uid);
    ClearWant(&want);
    return retVal;
}

int32 AbilityMgrFeature::ConnectAbility(const Want *want, SvcIdentity *svc, uint64_t token)
{
    return ConnectAbilityInner(want, svc, token, -1);
}

int32 AbilityMgrFeature::ConnectAbilityInner(const Want *want, SvcIdentity *svc, uint64_t token, pid_t callingUid)
{
    if (want == nullptr || want->element == nullptr || svc == nullptr) {
        return EC_INVALID;
    }
    Want *data = new Want();
    if (memset_s(data, sizeof(Want), 0, sizeof(Want)) != EOK) {
        delete data;
        PRINTE("AbilityMgrFeature", "memory alloc error");
        return EC_NOMEMORY;
    }
    SetWantElement(data, *(want->element));
    SetWantData(data, want->data, want->dataLength);
    if (want->sid != nullptr) {
        SetWantSvcIdentity(data, *(want->sid));
    }
    auto transParam = new AbilityConnectTransParam(data, *svc, token);
    transParam->SetCallingUid(callingUid);
    Request request = {
        .msgId = AMS_CONNECT_ABILITY,
        .len = 0,
        .data = reinterpret_cast<void *>(transParam),
    };
    int32 propRet = SAMGR_SendRequest(&(GetInstance()->identity_), &request, nullptr);
    if (propRet != EC_SUCCESS) {
        PRINTE("AbilityMgrFeature", "connect ability send request failure");
        delete transParam;
        return EC_COMMU;
    }
    return EC_SUCCESS;
}

int32 AbilityMgrFeature::DisconnectAbilityInvoke(const void *origin, IpcIo *req)
{
    uint64_t token = 0;
    ReadUint64(req, &token);
    SvcIdentity svc;
    bool ret = ReadRemoteObject(req, &svc);
    if (!ret) {
        return EC_INVALID;
    }
    return DisconnectAbility(&svc, token);
}

int32 AbilityMgrFeature::DisconnectAbility(SvcIdentity *svc, uint64_t token)
{
    if (svc == nullptr) {
        return EC_INVALID;
    }
    auto transParam = new AbilityConnectTransParam(nullptr, *svc, token);
    Request request = {
        .msgId = AMS_DISCONNECT_ABILITY,
        .len = 0,
        .data = reinterpret_cast<void *>(transParam),
    };
    int32 propRet = SAMGR_SendRequest(&(GetInstance()->identity_), &request, nullptr);
    if (propRet != EC_SUCCESS) {
        PRINTE("AbilityMgrFeature", "disconnect ability send request failure");
        delete transParam;
        return EC_COMMU;
    }
    return EC_SUCCESS;
}

int32 AbilityMgrFeature::ConnectAbilityDoneInvoke(const void *origin, IpcIo *req)
{
    uint64_t token = 0;
    ReadUint64(req, &token);
    SvcIdentity svc;
    bool ret = ReadRemoteObject(req, &svc);
    if (!ret) {
        return EC_INVALID;
    }

    auto transParam = new AbilityConnectTransParam(nullptr, svc, token);
    Request request = {
        .msgId = AMS_CONNECT_ABILITY_DONE,
        .len = 0,
        .data = reinterpret_cast<void *>(transParam),
    };
    int32 propRet = SAMGR_SendRequest(&(GetInstance()->identity_), &request, nullptr);
    if (propRet != EC_SUCCESS) {
        PRINTE("AbilityMgrFeature", "connect ability done send request failure");
        delete transParam;
        return EC_COMMU;
    }
    return EC_SUCCESS;
}

int32 AbilityMgrFeature::DisconnectAbilityDoneInvoke(const void *origin, IpcIo *req)
{
    uint64_t *disconnectToken = new uint64_t;
    uint64_t ret = 0;
    ReadUint64(req, &ret);
    *disconnectToken = ret;
    Request request = {
        .msgId = AMS_DISCONNECT_ABILITY_DONE,
        .len = 0,
        .data = reinterpret_cast<void *>(disconnectToken),
    };
    int32 propRet = SAMGR_SendRequest(&(GetInstance()->identity_), &request, nullptr);
    if (propRet != EC_SUCCESS) {
        PRINTE("AbilityMgrFeature", "disconnect ability done send request failure");
        delete disconnectToken;
        return EC_COMMU;
    }
    return EC_SUCCESS;
}

int32 AbilityMgrFeature::StopAbilityInvoke(const void *origin, IpcIo *req)
{
    pid_t uid = GetCallingUid();
    if (uid < 0) {
        PRINTE("AbilityMgrFeature", "invalid uid argument");
        return EC_INVALID;
    }
    Want want = { nullptr, nullptr, nullptr, 0 };
    if (!DeserializeWant(&want, req)) {
        return EC_FAILURE;
    }
    int retVal = StopAbilityInner(&want, uid);
    ClearWant(&want);
    return retVal;
}

int32 AbilityMgrFeature::StopAbility(const Want *want)
{
    return StopAbilityInner(want, -1);
}

int32 AbilityMgrFeature::StopAbilityInner(const Want *want, pid_t callingUid)
{
    if (want == nullptr || want->element == nullptr) {
        PRINTE("AbilityMgrFeature", "invalid argument");
        return EC_INVALID;
    }
    Want *data = new Want();
    if (memset_s(data, sizeof(Want), 0, sizeof(Want)) != EOK) {
        PRINTE("AbilityMgrFeature", "memory alloc error");
        delete data;
        return EC_NOMEMORY;
    }
    SetWantElement(data, *(want->element));
    SetWantData(data, want->data, want->dataLength);
    if (want->sid != nullptr) {
        SetWantSvcIdentity(data, *(want->sid));
    }
    Request request = {
        .msgId = AMS_TERMINATE_SERVICE,
        .len = 0,
        .data = reinterpret_cast<void *>(data),
        .msgValue = static_cast<uint32>(callingUid),
    };
    int32 propRet = SAMGR_SendRequest(&(GetInstance()->identity_), &request, nullptr);
    if (propRet != EC_SUCCESS) {
        PRINTE("AbilityMgrFeature", "send request failure");
        ClearWant(data);
        delete data;
        return EC_COMMU;
    }
    return EC_SUCCESS;
}

int32 AbilityMgrFeature::RestartApp(const char *bundleName)
{
    if (!AbilityMsHelper::IsLegalBundleName(bundleName)) {
        return EC_INVALID;
    }
    char *name = Utils::Strdup(bundleName);
    if (name == nullptr) {
        return EC_NOMEMORY;
    }
    Request request = {
        .msgId = AMS_RESTART_APP,
        .len = 0,
        .data = reinterpret_cast<void *>(name),
    };
    int32 propRet = SAMGR_SendRequest(&(GetInstance()->identity_), &request, nullptr);
    if (propRet != EC_SUCCESS) {
        PRINTE("AbilityMgrFeature", "send request failure");
        AdapterFree(name);
        return EC_COMMU;
    }
    return EC_SUCCESS;
}

void AbilityMgrFeature::AppDeathNotify(void *args)
{
    AppInfo *appInfo = reinterpret_cast<AppInfo *>(args);
    if (appInfo == nullptr) {
        return;
    }
    ReleaseSvc(appInfo->svcIdentity);
    if (!AbilityMsHelper::IsLegalBundleName(appInfo->bundleName)) {
        AdapterFree(appInfo->bundleName);
        delete appInfo;
        return;
    }
    PRINTE("AbilityMgrFeature", "%s AppDeathNotify called", appInfo->bundleName);
    int32 ret = RestartApp(appInfo->bundleName);
    if (ret != EC_SUCCESS) {
        PRINTE("AbilityMgrFeature", "restart app failure");
    }
    AdapterFree(appInfo->bundleName);
    delete appInfo;
}
} // namespace OHOS