/*
 * Copyright (c) 2023 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 "camera_device_fuzzer.h"
#include "camera_log.h"
#include "camera_xcollie.h"
#include "input/camera_manager.h"
#include "metadata_utils.h"
#include "ipc_skeleton.h"
#include "access_token.h"
#include "hap_token_info.h"
#include "accesstoken_kit.h"
#include "nativetoken_kit.h"
#include "token_setproc.h"
using namespace std;

namespace OHOS {
namespace CameraStandard {
const std::u16string FORMMGR_INTERFACE_TOKEN = u"ICameraDeviceService";
const size_t LIMITCOUNT = 4;
const int32_t NUM_2 = 2;
const int32_t NUM_10 = 10;
const int32_t NUM_100 = 100;
bool g_isCameraDevicePermission = false;
sptr<HCameraHostManager> fuzzCameraHostManager = nullptr;
HCameraDevice *fuzzCameraDevice = nullptr;

void CameraDeviceFuzzTestGetPermission()
{
    if (!g_isCameraDevicePermission) {
        uint64_t tokenId;
        const char *perms[0];
        perms[0] = "ohos.permission.CAMERA";
        NativeTokenInfoParams infoInstance = { .dcapsNum = 0, .permsNum = 1, .aclsNum = 0, .dcaps = NULL,
            .perms = perms, .acls = NULL, .processName = "camera_capture", .aplStr = "system_basic",
        };
        tokenId = GetAccessTokenId(&infoInstance);
        SetSelfTokenID(tokenId);
        OHOS::Security::AccessToken::AccessTokenKit::ReloadNativeTokenInfo();
        g_isCameraDevicePermission = true;
    }
}

void CameraDeviceFuzzTest(uint8_t *rawData, size_t size)
{
    if (rawData == nullptr || size < NUM_2) {
        return;
    }
    CameraDeviceFuzzTestGetPermission();

    int32_t itemCount = NUM_10;
    int32_t dataSize = NUM_100;
    int32_t *streams = reinterpret_cast<int32_t *>(rawData);
    std::shared_ptr<OHOS::Camera::CameraMetadata> ability;
    ability = std::make_shared<OHOS::Camera::CameraMetadata>(itemCount, dataSize);
    ability->addEntry(OHOS_ABILITY_STREAM_AVAILABLE_EXTEND_CONFIGURATIONS, streams, size / LIMITCOUNT);
    int32_t compensationRange[2] = {rawData[0], rawData[1]};
    ability->addEntry(OHOS_CONTROL_AE_COMPENSATION_RANGE, compensationRange,
                      sizeof(compensationRange) / sizeof(compensationRange[0]));
    float focalLength = rawData[0];
    ability->addEntry(OHOS_ABILITY_FOCAL_LENGTH, &focalLength, 1);

    int32_t sensorOrientation = rawData[0];
    ability->addEntry(OHOS_SENSOR_ORIENTATION, &sensorOrientation, 1);

    int32_t cameraPosition = rawData[0];
    ability->addEntry(OHOS_ABILITY_CAMERA_POSITION, &cameraPosition, 1);

    const camera_rational_t aeCompensationStep[] = {{rawData[0], rawData[1]}};
    ability->addEntry(OHOS_CONTROL_AE_COMPENSATION_STEP, &aeCompensationStep,
                      sizeof(aeCompensationStep) / sizeof(aeCompensationStep[0]));

    MessageParcel data;
    data.WriteInterfaceToken(FORMMGR_INTERFACE_TOKEN);
    CHECK_AND_RETURN_LOG(OHOS::Camera::MetadataUtils::EncodeCameraMetadata(ability, data),
        "CameraDeviceFuzzer: EncodeCameraMetadata Error");
    data.RewindRead(0);
    MessageParcel reply;
    MessageOption option;
    if (fuzzCameraDevice == nullptr || fuzzCameraHostManager == nullptr) {
        fuzzCameraHostManager = new(std::nothrow) HCameraHostManager(nullptr);
        if (fuzzCameraHostManager == nullptr) {
            return;
        }
        fuzzCameraDevice = new(std::nothrow) HCameraDevice(fuzzCameraHostManager, "", 0);
        if (fuzzCameraDevice == nullptr) {
            return;
        }
    }
    if (fuzzCameraDevice) {
        uint32_t code = 4;
        fuzzCameraDevice->OnRemoteRequest(code, data, reply, option);
    }
}

void CameraDeviceFuzzTestUpdateSetting(uint8_t *rawData, size_t size)
{
    if (rawData == nullptr || size < NUM_2) {
        return;
    }
    CameraDeviceFuzzTestGetPermission();

    int32_t itemCount = NUM_10;
    int32_t dataSize = NUM_100;
    int32_t *streams = reinterpret_cast<int32_t *>(rawData);
    auto ability = std::make_shared<OHOS::Camera::CameraMetadata>(itemCount, dataSize);
    ability->addEntry(OHOS_ABILITY_STREAM_AVAILABLE_EXTEND_CONFIGURATIONS, streams, size / LIMITCOUNT);
    int32_t compensationRange[2] = {rawData[0], rawData[1]};
    ability->addEntry(OHOS_CONTROL_AE_COMPENSATION_RANGE, compensationRange,
                      sizeof(compensationRange) / sizeof(compensationRange[0]));
    float focalLength = rawData[0];
    ability->addEntry(OHOS_ABILITY_FOCAL_LENGTH, &focalLength, 1);

    int32_t sensorOrientation = rawData[0];
    ability->addEntry(OHOS_SENSOR_ORIENTATION, &sensorOrientation, 1);

    int32_t cameraPosition = rawData[0];
    ability->addEntry(OHOS_ABILITY_CAMERA_POSITION, &cameraPosition, 1);

    const camera_rational_t aeCompensationStep[] = {{rawData[0], rawData[1]}};
    ability->addEntry(OHOS_CONTROL_AE_COMPENSATION_STEP, &aeCompensationStep,
                      sizeof(aeCompensationStep) / sizeof(aeCompensationStep[0]));
    if (fuzzCameraDevice) {
        fuzzCameraDevice->UpdateSettingOnce(ability);
        fuzzCameraDevice->UpdateSetting(ability);
        auto out = std::make_shared<OHOS::Camera::CameraMetadata>(itemCount, dataSize);
        fuzzCameraDevice->GetStatus(ability, out);
        std::vector<HDI::Camera::V1_1::StreamInfo_V1_1> streamInfos;
        fuzzCameraDevice->UpdateStreams(streamInfos);
        MessageParcel data;
        data.WriteRawData(rawData, size);
        fuzzCameraDevice->CreateAndCommitStreams(streamInfos, ability, data.ReadInt32());
        vector<uint8_t> result;
        OHOS::Camera::MetadataUtils::ConvertMetadataToVec(ability, result);
        fuzzCameraDevice->OnResult(data.ReadUint64(), result);
        auto type = OHOS::HDI::Camera::V1_0::ErrorType::REQUEST_TIMEOUT;
        fuzzCameraDevice->OnError(type, data.ReadInt32());
    }
}

void CameraDeviceFuzzTest2Case1(uint8_t *rawData, size_t size)
{
    MessageParcel data;
    data.WriteRawData(rawData, size);
    if (fuzzCameraDevice) {
        fuzzCameraDevice->GetStreamOperatorCallback();
        fuzzCameraDevice->GetCallerToken();
        fuzzCameraDevice->GetDeviceAbility();
        fuzzCameraDevice->GetCameraType();
        fuzzCameraDevice->GetCameraId();
        fuzzCameraDevice->GetStreamOperator();
    }
}

void CameraDeviceFuzzTest2Case2(uint8_t *rawData, size_t size)
{
    // 运行会出错
    MessageParcel data;
    data.WriteRawData(rawData, size);
    sptr<ICameraDeviceServiceCallback> callback(new ICameraDeviceServiceCallbackMock());
    fuzzCameraDevice->SetCallback(callback);
    wptr<IStreamOperatorCallback> opCallback(new IStreamOperatorCallbackMock());
    fuzzCameraDevice->SetStreamOperatorCallback(opCallback);
    vector<int32_t> results{data.ReadInt32()};
    fuzzCameraDevice->EnableResult(results);
    fuzzCameraDevice->DisableResult(results);
    fuzzCameraDevice->CloneCachedSettings();
    fuzzCameraDevice->DispatchDefaultSettingToHdi();
    fuzzCameraDevice->Release();
    fuzzCameraDevice->Close();
    uint64_t secureSeqId;
    fuzzCameraDevice->GetSecureCameraSeq(&secureSeqId);
    fuzzCameraDevice->OpenSecureCamera(&secureSeqId);
}

void CameraDeviceFuzzTest2Case3(uint8_t *rawData, size_t size)
{
    // 运行会出错
    MessageParcel data;
    data.WriteRawData(rawData, size);
    if (fuzzCameraDevice) {
        vector<int32_t> streamIds{data.ReadInt32()};
        fuzzCameraDevice->OnFrameShutter(data.ReadInt32(), streamIds, data.ReadUint64());
        fuzzCameraDevice->OnFrameShutterEnd(data.ReadInt32(), streamIds, data.ReadUint64());
        fuzzCameraDevice->OnCaptureReady(data.ReadInt32(), streamIds, data.ReadUint64());
        vector<OHOS::HDI::Camera::V1_2::CaptureStartedInfo> infos{{data.ReadInt32(), data.ReadInt32()}};
        fuzzCameraDevice->OnCaptureStarted_V1_2(data.ReadInt32(), infos);
        vector<CaptureEndedInfo> endedInfos{{data.ReadInt32(), data.ReadInt32()}};
        fuzzCameraDevice->OnCaptureEnded(data.ReadInt32(), endedInfos);
        vector<OHOS::HDI::Camera::V1_3::CaptureEndedInfoExt> endedInfosExt;
        fuzzCameraDevice->OnCaptureEndedExt(data.ReadInt32(), endedInfosExt);
        auto err = static_cast<OHOS::HDI::Camera::V1_0::StreamError>(data.ReadInt32());
        vector<CaptureErrorInfo> errorInfos{{data.ReadInt32(), err}};
        fuzzCameraDevice->OnCaptureError(data.ReadInt32(), errorInfos);
        fuzzCameraDevice->OnCaptureStarted(data.ReadInt32(), streamIds);
    }
}

void CameraDeviceFuzzTest2(uint8_t *rawData, size_t size)
{
    if (rawData == nullptr || size < NUM_2) {
        return;
    }
    MessageParcel data;
    data.WriteRawData(rawData, size);
    if (fuzzCameraDevice) {
        fuzzCameraDevice->OperatePermissionCheck(data.ReadUint32());
        fuzzCameraDevice->CheckMovingPhotoSupported(data.ReadInt32());
        fuzzCameraDevice->NotifyCameraStatus(data.ReadInt32());
        fuzzCameraDevice->RemoveResourceWhenHostDied();
        fuzzCameraDevice->NotifyCameraSessionStatus(data.ReadBool());
        std::vector<int32_t> releaseStreamIds;
        fuzzCameraDevice->ReleaseStreams(releaseStreamIds);
        fuzzCameraDevice->Open();
        CameraDeviceFuzzTest2Case1(rawData, size);
        fuzzCameraDevice->ResetDeviceSettings();
        fuzzCameraDevice->SetDeviceMuteMode(data.ReadBool());
        fuzzCameraDevice->IsOpenedCameraDevice();
        fuzzCameraDevice->CloseDevice();
    }
    fuzzCameraDevice = nullptr;
}

void GetPermission()
{
    uint64_t tokenId;
    const char* perms[2];
    perms[0] = "ohos.permission.DISTRIBUTED_DATASYNC";
    perms[1] = "ohos.permission.CAMERA";
    NativeTokenInfoParams infoInstance = {
        .dcapsNum = 0,
        .permsNum = 2,
        .aclsNum = 0,
        .dcaps = NULL,
        .perms = perms,
        .acls = NULL,
        .processName = "native_camera_tdd",
        .aplStr = "system_basic",
    };
    tokenId = GetAccessTokenId(&infoInstance);
    SetSelfTokenID(tokenId);
    OHOS::Security::AccessToken::AccessTokenKit::ReloadNativeTokenInfo();
}

void Test3(uint8_t *rawData, size_t size)
{
    if (rawData == nullptr || size < NUM_2) {
        return;
    }
    GetPermission();
    auto manager = CameraManager::GetInstance();
    auto cameras = manager->GetSupportedCameras();
    CHECK_AND_RETURN_LOG(cameras.size() >= NUM_2, "PhotoOutputFuzzer: GetSupportedCameras Error");
    MessageParcel data;
    data.WriteRawData(rawData, size);
    sptr<CameraDevice> camera = cameras[data.ReadUint32() % cameras.size()];
    camera->GetID();
    camera->GetMetadata();
    camera->ResetMetadata();
    camera->GetCameraAbility();
    camera->GetPosition();
    camera->GetCameraType();
    camera->GetConnectionType();
    camera->GetCameraFoldScreenType();
    camera->GetHostName();
    camera->GetDeviceType();
    camera->GetNetWorkId();
    camera->GetCameraOrientation();
    camera->GetZoomRatioRange();
    camera->GetExposureBiasRange();
    camera->GetModuleType();
    CameraFormat format = static_cast<CameraFormat>(data.ReadInt32());
    auto capability = manager->GetSupportedOutputCapability(camera);
    CHECK_AND_RETURN_LOG(capability, "PhotoOutputFuzzer: GetSupportedOutputCapability Error");
    vector<Profile> profiles = capability->GetPhotoProfiles();
    camera->GetMaxSizeProfile(profiles, data.ReadFloat(), format);
    auto profiles2 = capability->GetVideoProfiles();
    camera->GetMaxSizeProfile(profiles2, data.ReadFloat(), format);
}

void TestXCollie(uint8_t *rawData, size_t size)
{
    CHECK_ERROR_RETURN(rawData == nullptr || size < NUM_2);
    MessageParcel data;
    data.WriteRawData(rawData, size);
    string tag = data.ReadString();
    uint32_t flag = data.ReadInt32();
    uint32_t timeoutSeconds = data.ReadUint32();
    auto func = [](void*) {};
#ifndef HICOLLIE_ENABLE
#define HICOLLIE_ENABLE
#endif
    {
        CameraXCollie collie(tag, flag, timeoutSeconds, func, nullptr);
        collie.CancelCameraXCollie();
    }
#undef HICOLLIE_ENABLE
    {
        CameraXCollie collie(tag, flag, timeoutSeconds, func, nullptr);
        collie.CancelCameraXCollie();
    }
}
} // namespace CameraStandard
} // namespace OHOS

/* Fuzzer entry point */
extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size)
{
    /* Run your code on data */
    OHOS::CameraStandard::CameraDeviceFuzzTest(data, size);
    OHOS::CameraStandard::CameraDeviceFuzzTestUpdateSetting(data, size);
    OHOS::CameraStandard::CameraDeviceFuzzTest2(data, size);
    OHOS::CameraStandard::Test3(data, size);
    OHOS::CameraStandard::TestXCollie(data, size);
    return 0;
}