/*
 * Copyright (C) 2016 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 "SensorList.h"

#include <android/util/ProtoOutputStream.h>
#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
#include <hardware/sensors.h>
#include <utils/String8.h>

#include <cinttypes>

namespace android {
namespace SensorServiceUtil {

const Sensor SensorList::mNonSensor = Sensor("unknown");

bool SensorList::add(
        int handle, SensorInterface* si, bool isForDebug, bool isVirtual) {
    std::lock_guard<std::mutex> lk(mLock);
    if (handle == si->getSensor().getHandle() &&
        mUsedHandle.insert(handle).second) {
        // will succeed as the mUsedHandle does not have this handle
        mHandleMap.emplace(handle, Entry(si, isForDebug, isVirtual));
        return true;
    }
    // handle exist already or handle mismatch
    return false;
}

bool SensorList::remove(int handle) {
    std::lock_guard<std::mutex> lk(mLock);
    auto entry = mHandleMap.find(handle);
    if (entry != mHandleMap.end()) {
        mHandleMap.erase(entry);
        return true;
    }
    return false;
}

String8 SensorList::getName(int handle) const {
    return getOne<String8>(
            handle, [] (const Entry& e) -> String8 {return e.si->getSensor().getName();},
            mNonSensor.getName());
}

String8 SensorList::getStringType(int handle) const {
    return getOne<String8>(
            handle, [] (const Entry& e) -> String8 {return e.si->getSensor().getStringType();},
            mNonSensor.getStringType());
}

sp<SensorInterface> SensorList::getInterface(int handle) const {
    return getOne<sp<SensorInterface>>(
            handle, [] (const Entry& e) -> sp<SensorInterface> {return e.si;}, nullptr);
}


bool SensorList::isNewHandle(int handle) const {
    std::lock_guard<std::mutex> lk(mLock);
    return mUsedHandle.find(handle) == mUsedHandle.end();
}

const Vector<Sensor> SensorList::getUserSensors() const {
    // lock in forEachEntry
    Vector<Sensor> sensors;
    forEachEntry(
            [&sensors] (const Entry& e) -> bool {
                if (!e.isForDebug && !e.si->getSensor().isDynamicSensor()) {
                    sensors.add(e.si->getSensor());
                }
                return true;
            });
    return sensors;
}

const Vector<Sensor> SensorList::getUserDebugSensors() const {
    // lock in forEachEntry
    Vector<Sensor> sensors;
    forEachEntry(
            [&sensors] (const Entry& e) -> bool {
                if (!e.si->getSensor().isDynamicSensor()) {
                    sensors.add(e.si->getSensor());
                }
                return true;
            });
    return sensors;
}

const Vector<Sensor> SensorList::getDynamicSensors() const {
    // lock in forEachEntry
    Vector<Sensor> sensors;
    forEachEntry(
            [&sensors] (const Entry& e) -> bool {
                if (!e.isForDebug && e.si->getSensor().isDynamicSensor()) {
                    sensors.add(e.si->getSensor());
                }
                return true;
            });
    return sensors;
}

const Vector<Sensor> SensorList::getVirtualSensors() const {
    // lock in forEachEntry
    Vector<Sensor> sensors;
    forEachEntry(
            [&sensors] (const Entry& e) -> bool {
                if (e.isVirtual) {
                    sensors.add(e.si->getSensor());
                }
                return true;
            });
    return sensors;
}

std::string SensorList::dump() const {
    String8 result;

    forEachSensor([&result] (const Sensor& s) -> bool {
            result.appendFormat(
                    "%#010x) %-25s | %-15s | ver: %" PRId32 " | type: %20s(%" PRId32
                        ") | perm: %s | flags: 0x%08x\n",
                    s.getHandle(),
                    s.getName().string(),
                    s.getVendor().string(),
                    s.getVersion(),
                    s.getStringType().string(),
                    s.getType(),
                    s.getRequiredPermission().size() ? s.getRequiredPermission().string() : "n/a",
                    static_cast<int>(s.getFlags()));

            result.append("\t");
            const int reportingMode = s.getReportingMode();
            if (reportingMode == AREPORTING_MODE_CONTINUOUS) {
                result.append("continuous | ");
            } else if (reportingMode == AREPORTING_MODE_ON_CHANGE) {
                result.append("on-change | ");
            } else if (reportingMode == AREPORTING_MODE_ONE_SHOT) {
                result.append("one-shot | ");
            } else if (reportingMode == AREPORTING_MODE_SPECIAL_TRIGGER) {
                result.append("special-trigger | ");
            } else {
                result.append("unknown-mode | ");
            }

            if (s.getMaxDelay() > 0) {
                result.appendFormat("minRate=%.2fHz | ", 1e6f / s.getMaxDelay());
            } else {
                result.appendFormat("maxDelay=%" PRId32 "us | ", s.getMaxDelay());
            }

            if (s.getMinDelay() > 0) {
                result.appendFormat("maxRate=%.2fHz | ", 1e6f / s.getMinDelay());
            } else {
                result.appendFormat("minDelay=%" PRId32 "us | ", s.getMinDelay());
            }

            if (s.getFifoMaxEventCount() > 0) {
                result.appendFormat("FIFO (max,reserved) = (%" PRIu32 ", %" PRIu32 ") events | ",
                        s.getFifoMaxEventCount(),
                        s.getFifoReservedEventCount());
            } else {
                result.append("no batching | ");
            }

            if (s.isWakeUpSensor()) {
                result.appendFormat("wakeUp | ");
            } else {
                result.appendFormat("non-wakeUp | ");
            }

            if (s.isDataInjectionSupported()) {
                result.appendFormat("data-injection, ");
            }

            if (s.isDynamicSensor()) {
                result.appendFormat("dynamic, ");
            }

            if (s.hasAdditionalInfo()) {
                result.appendFormat("has-additional-info, ");
            }
            result.append("\n");

            if (s.getHighestDirectReportRateLevel() > SENSOR_DIRECT_RATE_STOP) {
                result.appendFormat("\thighest rate level = %d, support shared mem: ",
                        s.getHighestDirectReportRateLevel());
                if (s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_ASHMEM)) {
                    result.append("ashmem, ");
                }
                if (s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_GRALLOC)) {
                    result.append("gralloc, ");
                }
                result.append("\n");
            }
            return true;
        });
    return std::string(result.string());
}

/**
 * Dump debugging information as android.service.SensorListProto protobuf message using
 * ProtoOutputStream.
 *
 * See proto definition and some notes about ProtoOutputStream in
 * frameworks/base/core/proto/android/service/sensor_service.proto
 */
void SensorList::dump(util::ProtoOutputStream* proto) const {
    using namespace service::SensorListProto;
    using namespace service::SensorListProto::SensorProto;

    forEachSensor([&proto] (const Sensor& s) -> bool {
        const uint64_t token = proto->start(SENSORS);
        proto->write(HANDLE, s.getHandle());
        proto->write(NAME, std::string(s.getName().string()));
        proto->write(VENDOR, std::string(s.getVendor().string()));
        proto->write(VERSION, s.getVersion());
        proto->write(STRING_TYPE, std::string(s.getStringType().string()));
        proto->write(TYPE, s.getType());
        proto->write(REQUIRED_PERMISSION, std::string(s.getRequiredPermission().size() ?
                s.getRequiredPermission().string() : ""));
        proto->write(FLAGS, int(s.getFlags()));
        switch (s.getReportingMode()) {
            case AREPORTING_MODE_CONTINUOUS:
                proto->write(REPORTING_MODE, RM_CONTINUOUS);
                break;
            case AREPORTING_MODE_ON_CHANGE:
                proto->write(REPORTING_MODE, RM_ON_CHANGE);
                break;
            case AREPORTING_MODE_ONE_SHOT:
                proto->write(REPORTING_MODE, RM_ONE_SHOT);
                break;
            case AREPORTING_MODE_SPECIAL_TRIGGER:
                proto->write(REPORTING_MODE, RM_SPECIAL_TRIGGER);
                break;
            default:
                proto->write(REPORTING_MODE, RM_UNKNOWN);
        }
        proto->write(MAX_DELAY_US, s.getMaxDelay());
        proto->write(MIN_DELAY_US, s.getMinDelay());
        proto->write(FIFO_MAX_EVENT_COUNT, int(s.getFifoMaxEventCount()));
        proto->write(FIFO_RESERVED_EVENT_COUNT, int(s.getFifoReservedEventCount()));
        proto->write(IS_WAKEUP, s.isWakeUpSensor());
        proto->write(DATA_INJECTION_SUPPORTED, s.isDataInjectionSupported());
        proto->write(IS_DYNAMIC, s.isDynamicSensor());
        proto->write(HAS_ADDITIONAL_INFO, s.hasAdditionalInfo());
        proto->write(HIGHEST_RATE_LEVEL, s.getHighestDirectReportRateLevel());
        proto->write(ASHMEM, s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_ASHMEM));
        proto->write(GRALLOC, s.isDirectChannelTypeSupported(SENSOR_DIRECT_MEM_TYPE_GRALLOC));
        proto->write(MIN_VALUE, s.getMinValue());
        proto->write(MAX_VALUE, s.getMaxValue());
        proto->write(RESOLUTION, s.getResolution());
        proto->write(POWER_USAGE, s.getPowerUsage());
        proto->end(token);
        return true;
    });
}

SensorList::~SensorList() {
}

} // namespace SensorServiceUtil
} // namespace android

