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

#pragma once

#include <android-base/unique_fd.h>
#include <cutils/ashmem.h>
#include <fmq/EventFlag.h>
#include <sys/mman.h>
#include <sys/user.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
#include <atomic>
#include <functional>

using android::hardware::kSynchronizedReadWrite;
using android::hardware::kUnsynchronizedWrite;
using android::hardware::MQFlavor;

namespace android {

/* sentinel payload type that indicates the MQ will be used with a mismatching
MQDescriptor type, where type safety must be enforced elsewhere because the real
element type T is not statically known. This is used to instantiate
MessageQueueBase instances for Rust where we cannot generate additional template
instantiations across the language boundary. */
enum MQErased {};

template <template <typename, MQFlavor> class MQDescriptorType, typename T, MQFlavor flavor>
struct MessageQueueBase {
    typedef MQDescriptorType<T, flavor> Descriptor;
    enum Error : int {
        NONE,
        POINTER_CORRUPTION, /** Read/write pointers mismatch */
    };
    using ErrorHandler = std::function<void(Error, std::string&&)>;

    /**
     * @param Desc MQDescriptor describing the FMQ.
     * @param resetPointers bool indicating whether the read/write pointers
     * should be reset or not.
     */
    MessageQueueBase(const Descriptor& Desc, bool resetPointers = true);

    ~MessageQueueBase();

    /**
     * This constructor uses Ashmem shared memory to create an FMQ
     * that can contain a maximum of 'numElementsInQueue' elements of type T.
     *
     * @param numElementsInQueue Capacity of the MessageQueue in terms of T.
     * @param configureEventFlagWord Boolean that specifies if memory should
     * also be allocated and mapped for an EventFlag word.
     * @param bufferFd User-supplied file descriptor to map the memory for the ringbuffer
     * By default, bufferFd=-1 means library will allocate ashmem region for ringbuffer.
     * MessageQueue takes ownership of the file descriptor.
     * @param bufferSize size of buffer in bytes that bufferFd represents. This
     * size must be larger than or equal to (numElementsInQueue * sizeof(T)).
     * Otherwise, operations will cause out-of-bounds memory access.
     */

    MessageQueueBase(size_t numElementsInQueue, bool configureEventFlagWord,
                     android::base::unique_fd bufferFd, size_t bufferSize)
        : MessageQueueBase(numElementsInQueue, configureEventFlagWord, std::move(bufferFd),
                           bufferSize, sizeof(T)) {
        /* We must not pass sizeof(T) as quantum for MQErased element type. */
        static_assert(!std::is_same_v<T, MQErased>,
                      "MessageQueueBase<..., MQErased, ...> must be constructed via a"
                      " constructor that accepts a descriptor or a quantum size");
    };

    MessageQueueBase(size_t numElementsInQueue, bool configureEventFlagWord = false)
        : MessageQueueBase(numElementsInQueue, configureEventFlagWord, android::base::unique_fd(),
                           0) {}

    /**
     * Set a client side error handler function which will be invoked when the FMQ detects
     * one of the error situations defined by the 'Error' type.
     */
    void setErrorHandler(ErrorHandler&& handler) { mErrorHandler.swap(handler); }

    /**
     * @return Number of items of type T that can be written into the FMQ
     * without a read.
     */
    size_t availableToWrite() const;

    /**
     * @return Number of items of type T that are waiting to be read from the
     * FMQ.
     */
    size_t availableToRead() const;

    /**
     * Returns the size of type T in bytes.
     *
     * @return Size of T.
     */
    size_t getQuantumSize() const;

    /**
     * Returns the size of the FMQ in terms of the size of type T.
     *
     * @return Number of items of type T that will fit in the FMQ.
     */
    size_t getQuantumCount() const;

    /**
     * @return Whether the FMQ is configured correctly.
     */
    bool isValid() const;

    /**
     * Non-blocking write to FMQ.
     *
     * @param data Pointer to the object of type T to be written into the FMQ.
     *
     * @return Whether the write was successful.
     */
    bool write(const T* data);

    /**
     * Non-blocking read from FMQ.
     *
     * @param data Pointer to the memory where the object read from the FMQ is
     * copied to.
     *
     * @return Whether the read was successful.
     */
    bool read(T* data);

    /**
     * Write some data into the FMQ without blocking.
     *
     * @param data Pointer to the array of items of type T.
     * @param count Number of items in array.
     *
     * @return Whether the write was successful.
     */
    bool write(const T* data, size_t count);

    /**
     * Perform a blocking write of 'count' items into the FMQ using EventFlags.
     * Does not support partial writes.
     *
     * If 'evFlag' is nullptr, it is checked whether there is an EventFlag object
     * associated with the FMQ and it is used in that case.
     *
     * The application code must ensure that 'evFlag' used by the
     * reader(s)/writer is based upon the same EventFlag word.
     *
     * The method will return false without blocking if any of the following
     * conditions are true:
     * - If 'evFlag' is nullptr and the FMQ does not own an EventFlag object.
     * - If the 'readNotification' bit mask is zero.
     * - If 'count' is greater than the FMQ size.
     *
     * If the there is insufficient space available to write into it, the
     * EventFlag bit mask 'readNotification' is is waited upon.
     *
     * This method should only be used with a MessageQueue of the flavor
     * 'kSynchronizedReadWrite'.
     *
     * Upon a successful write, wake is called on 'writeNotification' (if
     * non-zero).
     *
     * @param data Pointer to the array of items of type T.
     * @param count Number of items in array.
     * @param readNotification The EventFlag bit mask to wait on if there is not
     * enough space in FMQ to write 'count' items.
     * @param writeNotification The EventFlag bit mask to call wake on
     * a successful write. No wake is called if 'writeNotification' is zero.
     * @param timeOutNanos Number of nanoseconds after which the blocking
     * write attempt is aborted.
     * @param evFlag The EventFlag object to be used for blocking. If nullptr,
     * it is checked whether the FMQ owns an EventFlag object and that is used
     * for blocking instead.
     *
     * @return Whether the write was successful.
     */
    bool writeBlocking(const T* data, size_t count, uint32_t readNotification,
                       uint32_t writeNotification, int64_t timeOutNanos = 0,
                       android::hardware::EventFlag* evFlag = nullptr);

    bool writeBlocking(const T* data, size_t count, int64_t timeOutNanos = 0);

    /**
     * Read some data from the FMQ without blocking.
     *
     * @param data Pointer to the array to which read data is to be written.
     * @param count Number of items to be read.
     *
     * @return Whether the read was successful.
     */
    bool read(T* data, size_t count);

    /**
     * Perform a blocking read operation of 'count' items from the FMQ. Does not
     * perform a partial read.
     *
     * If 'evFlag' is nullptr, it is checked whether there is an EventFlag object
     * associated with the FMQ and it is used in that case.
     *
     * The application code must ensure that 'evFlag' used by the
     * reader(s)/writer is based upon the same EventFlag word.
     *
     * The method will return false without blocking if any of the following
     * conditions are true:
     * -If 'evFlag' is nullptr and the FMQ does not own an EventFlag object.
     * -If the 'writeNotification' bit mask is zero.
     * -If 'count' is greater than the FMQ size.
     *
     * This method should only be used with a MessageQueue of the flavor
     * 'kSynchronizedReadWrite'.

     * If FMQ does not contain 'count' items, the eventFlag bit mask
     * 'writeNotification' is waited upon. Upon a successful read from the FMQ,
     * wake is called on 'readNotification' (if non-zero).
     *
     * @param data Pointer to the array to which read data is to be written.
     * @param count Number of items to be read.
     * @param readNotification The EventFlag bit mask to call wake on after
     * a successful read. No wake is called if 'readNotification' is zero.
     * @param writeNotification The EventFlag bit mask to call a wait on
     * if there is insufficient data in the FMQ to be read.
     * @param timeOutNanos Number of nanoseconds after which the blocking
     * read attempt is aborted.
     * @param evFlag The EventFlag object to be used for blocking.
     *
     * @return Whether the read was successful.
     */
    bool readBlocking(T* data, size_t count, uint32_t readNotification, uint32_t writeNotification,
                      int64_t timeOutNanos = 0, android::hardware::EventFlag* evFlag = nullptr);

    bool readBlocking(T* data, size_t count, int64_t timeOutNanos = 0);

    /**
     * Get a pointer to the MQDescriptor object that describes this FMQ.
     *
     * @return Pointer to the MQDescriptor associated with the FMQ.
     */
    const Descriptor* getDesc() const { return mDesc.get(); }

    /**
     * Get a pointer to the EventFlag word if there is one associated with this FMQ.
     *
     * @return Pointer to an EventFlag word, will return nullptr if not
     * configured. This method does not transfer ownership. The EventFlag
     * word will be unmapped by the MessageQueue destructor.
     */
    std::atomic<uint32_t>* getEventFlagWord() const { return mEvFlagWord; }

    /**
     * Describes a memory region in the FMQ.
     */
    struct MemRegion {
        MemRegion() : MemRegion(nullptr, 0) {}

        MemRegion(T* base, size_t size) : address(base), length(size) {}

        MemRegion& operator=(const MemRegion& other) {
            address = other.address;
            length = other.length;
            return *this;
        }

        /**
         * Gets a pointer to the base address of the MemRegion.
         */
        inline T* getAddress() const { return address; }

        /**
         * Gets the length of the MemRegion. This would equal to the number
         * of items of type T that can be read from/written into the MemRegion.
         */
        inline size_t getLength() const { return length; }

        /**
         * Gets the length of the MemRegion in bytes.
         */
        template <class U = T>
        inline std::enable_if_t<!std::is_same_v<U, MQErased>, size_t> getLengthInBytes() const {
            return length * kQuantumValue<U>;
        }

      private:
        /* Base address */
        T* address;

        /*
         * Number of items of type T that can be written to/read from the base
         * address.
         */
        size_t length;
    };

    /**
     * Describes the memory regions to be used for a read or write.
     * The struct contains two MemRegion objects since the FMQ is a ring
     * buffer and a read or write operation can wrap around. A single message
     * of type T will never be broken between the two MemRegions.
     */
    struct MemTransaction {
        MemTransaction() : MemTransaction(MemRegion(), MemRegion()) {}

        MemTransaction(const MemRegion& regionFirst, const MemRegion& regionSecond)
            : first(regionFirst), second(regionSecond) {}

        MemTransaction& operator=(const MemTransaction& other) {
            first = other.first;
            second = other.second;
            return *this;
        }

        /**
         * Helper method to calculate the address for a particular index for
         * the MemTransaction object.
         *
         * @param idx Index of the slot to be read/written. If the
         * MemTransaction object is representing the memory region to read/write
         * N items of type T, the valid range of idx is between 0 and N-1.
         *
         * @return Pointer to the slot idx. Will be nullptr for an invalid idx.
         */
        T* getSlot(size_t idx);

        /**
         * Helper method to write 'nMessages' items of type T into the memory
         * regions described by the object starting from 'startIdx'. This method
         * uses memcpy() and is not to meant to be used for a zero copy operation.
         * Partial writes are not supported.
         *
         * @param data Pointer to the source buffer.
         * @param nMessages Number of items of type T.
         * @param startIdx The slot number to begin the write from. If the
         * MemTransaction object is representing the memory region to read/write
         * N items of type T, the valid range of startIdx is between 0 and N-1;
         *
         * @return Whether the write operation of size 'nMessages' succeeded.
         */
        bool copyTo(const T* data, size_t startIdx, size_t nMessages = 1);

        /*
         * Helper method to read 'nMessages' items of type T from the memory
         * regions described by the object starting from 'startIdx'. This method uses
         * memcpy() and is not meant to be used for a zero copy operation. Partial reads
         * are not supported.
         *
         * @param data Pointer to the destination buffer.
         * @param nMessages Number of items of type T.
         * @param startIdx The slot number to begin the read from. If the
         * MemTransaction object is representing the memory region to read/write
         * N items of type T, the valid range of startIdx is between 0 and N-1.
         *
         * @return Whether the read operation of size 'nMessages' succeeded.
         */
        bool copyFrom(T* data, size_t startIdx, size_t nMessages = 1);

        /**
         * Returns a const reference to the first MemRegion in the
         * MemTransaction object.
         */
        inline const MemRegion& getFirstRegion() const { return first; }

        /**
         * Returns a const reference to the second MemRegion in the
         * MemTransaction object.
         */
        inline const MemRegion& getSecondRegion() const { return second; }

      private:
        friend MessageQueueBase<MQDescriptorType, T, flavor>;

        bool copyToSized(const T* data, size_t startIdx, size_t nMessages, size_t messageSize);
        bool copyFromSized(T* data, size_t startIdx, size_t nMessages, size_t messageSize);

        /*
         * Given a start index and the number of messages to be
         * read/written, this helper method calculates the
         * number of messages that should should be written to both the first
         * and second MemRegions and the base addresses to be used for
         * the read/write operation.
         *
         * Returns false if the 'startIdx' and 'nMessages' is
         * invalid for the MemTransaction object.
         */
        bool inline getMemRegionInfo(size_t idx, size_t nMessages, size_t& firstCount,
                                     size_t& secondCount, T** firstBaseAddress,
                                     T** secondBaseAddress);
        MemRegion first;
        MemRegion second;
    };

    /**
     * Get a MemTransaction object to write 'nMessages' items of type T.
     * Once the write is performed using the information from MemTransaction,
     * the write operation is to be committed using a call to commitWrite().
     *
     * @param nMessages Number of messages of type T.
     * @param Pointer to MemTransaction struct that describes memory to write 'nMessages'
     * items of type T. If a write of size 'nMessages' is not possible, the base
     * addresses in the MemTransaction object would be set to nullptr.
     *
     * @return Whether it is possible to write 'nMessages' items of type T
     * into the FMQ.
     */
    bool beginWrite(size_t nMessages, MemTransaction* memTx) const;

    /**
     * Commit a write of size 'nMessages'. To be only used after a call to beginWrite().
     *
     * @param nMessages number of messages of type T to be written.
     *
     * @return Whether the write operation of size 'nMessages' succeeded.
     */
    bool commitWrite(size_t nMessages);

    /**
     * Get a MemTransaction object to read 'nMessages' items of type T.
     * Once the read is performed using the information from MemTransaction,
     * the read operation is to be committed using a call to commitRead().
     *
     * @param nMessages Number of messages of type T.
     * @param pointer to MemTransaction struct that describes memory to read 'nMessages'
     * items of type T. If a read of size 'nMessages' is not possible, the base
     * pointers in the MemTransaction object returned will be set to nullptr.
     *
     * @return bool Whether it is possible to read 'nMessages' items of type T
     * from the FMQ.
     */
    bool beginRead(size_t nMessages, MemTransaction* memTx) const;

    /**
     * Commit a read of size 'nMessages'. To be only used after a call to beginRead().
     * For the unsynchronized flavor of FMQ, this method will return a failure
     * if a write overflow happened after beginRead() was invoked.
     *
     * @param nMessages number of messages of type T to be read.
     *
     * @return bool Whether the read operation of size 'nMessages' succeeded.
     */
    bool commitRead(size_t nMessages);

    /**
     * Get the pointer to the ring buffer. Useful for debugging and fuzzing.
     */
    uint8_t* getRingBufferPtr() const { return mRing; }

  protected:
    /**
     * Protected constructor that can manually specify the quantum to use.
     * The only external consumer of this ctor is ErasedMessageQueue, but the
     * constructor cannot be private because this is a base class.
     *
     * @param quantum Size of the element type, in bytes.
     * Other parameters have semantics given in the corresponding public ctor.
     */

    MessageQueueBase(size_t numElementsInQueue, bool configureEventFlagWord,
                     android::base::unique_fd bufferFd, size_t bufferSize, size_t quantum);

  private:
    template <class U = T,
              typename std::enable_if<!std::is_same<U, MQErased>::value, bool>::type = true>
    static constexpr size_t kQuantumValue = sizeof(T);
    inline size_t quantum() const;
    size_t availableToWriteBytes() const;
    size_t availableToReadBytes() const;

    MessageQueueBase(const MessageQueueBase& other) = delete;
    MessageQueueBase& operator=(const MessageQueueBase& other) = delete;

    void* mapGrantorDescr(uint32_t grantorIdx);
    void unmapGrantorDescr(void* address, uint32_t grantorIdx);
    void initMemory(bool resetPointers);
    bool processOverflow(uint64_t readPtr, uint64_t writePtr) const;

    enum DefaultEventNotification : uint32_t {
        /*
         * These are only used internally by the readBlocking()/writeBlocking()
         * methods and hence once other bit combinations are not required.
         */
        FMQ_NOT_FULL = 0x01,
        FMQ_NOT_EMPTY = 0x02
    };
    std::unique_ptr<Descriptor> mDesc;
    uint8_t* mRing = nullptr;
    /*
     * TODO(b/31550092): Change to 32 bit read and write pointer counters.
     */
    std::atomic<uint64_t>* mReadPtr = nullptr;
    std::atomic<uint64_t>* mWritePtr = nullptr;

    std::atomic<uint32_t>* mEvFlagWord = nullptr;

    /*
     * This EventFlag object will be owned by the FMQ and will have the same
     * lifetime.
     */
    android::hardware::EventFlag* mEventFlag = nullptr;

    ErrorHandler mErrorHandler;

    const size_t kPageSize = getpagesize();
};

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
T* MessageQueueBase<MQDescriptorType, T, flavor>::MemTransaction::getSlot(size_t idx) {
    size_t firstRegionLength = first.getLength();
    size_t secondRegionLength = second.getLength();

    if (idx > firstRegionLength + secondRegionLength) {
        return nullptr;
    }

    if (idx < firstRegionLength) {
        return first.getAddress() + idx;
    }

    return second.getAddress() + idx - firstRegionLength;
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
bool MessageQueueBase<MQDescriptorType, T, flavor>::MemTransaction::getMemRegionInfo(
        size_t startIdx, size_t nMessages, size_t& firstCount, size_t& secondCount,
        T** firstBaseAddress, T** secondBaseAddress) {
    size_t firstRegionLength = first.getLength();
    size_t secondRegionLength = second.getLength();

    if (startIdx + nMessages > firstRegionLength + secondRegionLength) {
        /*
         * Return false if 'nMessages' starting at 'startIdx' cannot be
         * accommodated by the MemTransaction object.
         */
        return false;
    }

    /* Number of messages to be read/written to the first MemRegion. */
    firstCount =
            startIdx < firstRegionLength ? std::min(nMessages, firstRegionLength - startIdx) : 0;

    /* Number of messages to be read/written to the second MemRegion. */
    secondCount = nMessages - firstCount;

    if (firstCount != 0) {
        *firstBaseAddress = first.getAddress() + startIdx;
    }

    if (secondCount != 0) {
        size_t secondStartIdx = startIdx > firstRegionLength ? startIdx - firstRegionLength : 0;
        *secondBaseAddress = second.getAddress() + secondStartIdx;
    }

    return true;
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
bool MessageQueueBase<MQDescriptorType, T, flavor>::MemTransaction::copyFrom(T* data,
                                                                             size_t startIdx,
                                                                             size_t nMessages) {
    if constexpr (!std::is_same<T, MQErased>::value) {
        return copyFromSized(data, startIdx, nMessages, kQuantumValue<T>);
    } else {
        /* Compile error. */
        static_assert(!std::is_same<T, MQErased>::value,
                      "copyFrom without messageSize argument cannot be used with MQErased (use "
                      "copyFromSized)");
    }
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
bool MessageQueueBase<MQDescriptorType, T, flavor>::MemTransaction::copyFromSized(
        T* data, size_t startIdx, size_t nMessages, size_t messageSize) {
    if (data == nullptr) {
        return false;
    }

    size_t firstReadCount = 0, secondReadCount = 0;
    T *firstBaseAddress = nullptr, *secondBaseAddress = nullptr;

    if (getMemRegionInfo(startIdx, nMessages, firstReadCount, secondReadCount, &firstBaseAddress,
                         &secondBaseAddress) == false) {
        /*
         * Returns false if 'startIdx' and 'nMessages' are invalid for this
         * MemTransaction object.
         */
        return false;
    }

    if (firstReadCount != 0) {
        memcpy(data, firstBaseAddress, firstReadCount * messageSize);
    }

    if (secondReadCount != 0) {
        memcpy(data + firstReadCount, secondBaseAddress, secondReadCount * messageSize);
    }

    return true;
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
bool MessageQueueBase<MQDescriptorType, T, flavor>::MemTransaction::copyTo(const T* data,
                                                                           size_t startIdx,
                                                                           size_t nMessages) {
    if constexpr (!std::is_same<T, MQErased>::value) {
        return copyToSized(data, startIdx, nMessages, kQuantumValue<T>);
    } else {
        /* Compile error. */
        static_assert(!std::is_same<T, MQErased>::value,
                      "copyTo without messageSize argument cannot be used with MQErased (use "
                      "copyToSized)");
    }
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
bool MessageQueueBase<MQDescriptorType, T, flavor>::MemTransaction::copyToSized(
        const T* data, size_t startIdx, size_t nMessages, size_t messageSize) {
    if (data == nullptr) {
        return false;
    }

    size_t firstWriteCount = 0, secondWriteCount = 0;
    T *firstBaseAddress = nullptr, *secondBaseAddress = nullptr;

    if (getMemRegionInfo(startIdx, nMessages, firstWriteCount, secondWriteCount, &firstBaseAddress,
                         &secondBaseAddress) == false) {
        /*
         * Returns false if 'startIdx' and 'nMessages' are invalid for this
         * MemTransaction object.
         */
        return false;
    }

    if (firstWriteCount != 0) {
        memcpy(firstBaseAddress, data, firstWriteCount * messageSize);
    }

    if (secondWriteCount != 0) {
        memcpy(secondBaseAddress, data + firstWriteCount, secondWriteCount * messageSize);
    }

    return true;
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
void MessageQueueBase<MQDescriptorType, T, flavor>::initMemory(bool resetPointers) {
    /*
     * Verify that the Descriptor contains the minimum number of grantors
     * the native_handle is valid and T matches quantum size.
     */
    if ((mDesc == nullptr) || !mDesc->isHandleValid() ||
        (mDesc->countGrantors() < hardware::details::kMinGrantorCount)) {
        return;
    }
    if (mDesc->getQuantum() != quantum()) {
        hardware::details::logError(
                "Payload size differs between the queue instantiation and the "
                "MQDescriptor.");
        return;
    }

    if (flavor == kSynchronizedReadWrite) {
        mReadPtr = reinterpret_cast<std::atomic<uint64_t>*>(
                mapGrantorDescr(hardware::details::READPTRPOS));
    } else {
        /*
         * The unsynchronized write flavor of the FMQ may have multiple readers
         * and each reader would have their own read pointer counter.
         */
        mReadPtr = new (std::nothrow) std::atomic<uint64_t>;
    }
    if (mReadPtr == nullptr) goto error;

    mWritePtr = reinterpret_cast<std::atomic<uint64_t>*>(
            mapGrantorDescr(hardware::details::WRITEPTRPOS));
    if (mWritePtr == nullptr) goto error;

    if (resetPointers) {
        mReadPtr->store(0, std::memory_order_release);
        mWritePtr->store(0, std::memory_order_release);
    } else if (flavor != kSynchronizedReadWrite) {
        // Always reset the read pointer.
        mReadPtr->store(0, std::memory_order_release);
    }

    mRing = reinterpret_cast<uint8_t*>(mapGrantorDescr(hardware::details::DATAPTRPOS));
    if (mRing == nullptr) goto error;

    if (mDesc->countGrantors() > hardware::details::EVFLAGWORDPOS) {
        mEvFlagWord = static_cast<std::atomic<uint32_t>*>(
                mapGrantorDescr(hardware::details::EVFLAGWORDPOS));
        if (mEvFlagWord == nullptr) goto error;
        android::hardware::EventFlag::createEventFlag(mEvFlagWord, &mEventFlag);
    }
    return;
error:
    if (mReadPtr) {
        if (flavor == kSynchronizedReadWrite) {
            unmapGrantorDescr(mReadPtr, hardware::details::READPTRPOS);
        } else {
            delete mReadPtr;
        }
        mReadPtr = nullptr;
    }
    if (mWritePtr) {
        unmapGrantorDescr(mWritePtr, hardware::details::WRITEPTRPOS);
        mWritePtr = nullptr;
    }
    if (mRing) {
        unmapGrantorDescr(mRing, hardware::details::EVFLAGWORDPOS);
        mRing = nullptr;
    }
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
MessageQueueBase<MQDescriptorType, T, flavor>::MessageQueueBase(const Descriptor& Desc,
                                                                bool resetPointers) {
    mDesc = std::unique_ptr<Descriptor>(new (std::nothrow) Descriptor(Desc));
    if (mDesc == nullptr || mDesc->getSize() == 0) {
        hardware::details::logError("MQDescriptor is invalid or queue size is 0.");
        return;
    }

    initMemory(resetPointers);
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
MessageQueueBase<MQDescriptorType, T, flavor>::MessageQueueBase(size_t numElementsInQueue,
                                                                bool configureEventFlagWord,
                                                                android::base::unique_fd bufferFd,
                                                                size_t bufferSize, size_t quantum) {
    // Check if the buffer size would not overflow size_t
    if (numElementsInQueue > SIZE_MAX / quantum) {
        hardware::details::logError("Requested message queue size too large. Size of elements: " +
                                    std::to_string(quantum) +
                                    ". Number of elements: " + std::to_string(numElementsInQueue));
        return;
    }
    if (numElementsInQueue == 0) {
        hardware::details::logError("Requested queue size of 0.");
        return;
    }
    if (bufferFd != -1 && numElementsInQueue * quantum > bufferSize) {
        hardware::details::logError("The supplied buffer size(" + std::to_string(bufferSize) +
                                    ") is smaller than the required size(" +
                                    std::to_string(numElementsInQueue * quantum) + ").");
        return;
    }
    /*
     * The FMQ needs to allocate memory for the ringbuffer as well as for the
     * read and write pointer counters. If an EventFlag word is to be configured,
     * we also need to allocate memory for the same/
     */
    size_t kQueueSizeBytes = numElementsInQueue * quantum;
    size_t kMetaDataSize = 2 * sizeof(android::hardware::details::RingBufferPosition);

    if (configureEventFlagWord) {
        kMetaDataSize += sizeof(std::atomic<uint32_t>);
    }

    /*
     * Ashmem memory region size needs to be specified in page-aligned bytes.
     * kQueueSizeBytes needs to be aligned to word boundary so that all offsets
     * in the grantorDescriptor will be word aligned.
     */
    size_t kAshmemSizePageAligned;
    if (bufferFd != -1) {
        // Allocate read counter and write counter only. User-supplied memory will be used for the
        // ringbuffer.
        kAshmemSizePageAligned = (kMetaDataSize + kPageSize - 1) & ~(kPageSize - 1);
    } else {
        // Allocate ringbuffer, read counter and write counter.
        kAshmemSizePageAligned = (hardware::details::alignToWordBoundary(kQueueSizeBytes) +
                                  kMetaDataSize + kPageSize - 1) &
                                 ~(kPageSize - 1);
    }

    /*
     * The native handle will contain the fds to be mapped.
     */
    int numFds = (bufferFd != -1) ? 2 : 1;
    native_handle_t* mqHandle = native_handle_create(numFds, 0 /* numInts */);
    if (mqHandle == nullptr) {
        return;
    }

    /*
     * Create an ashmem region to map the memory.
     */
    int ashmemFd = ashmem_create_region("MessageQueue", kAshmemSizePageAligned);
    ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
    mqHandle->data[0] = ashmemFd;

    if (bufferFd != -1) {
        // Use user-supplied file descriptor for fdIndex 1
        mqHandle->data[1] = bufferFd.get();
        // release ownership of fd. mqHandle owns it now.
        if (bufferFd.release() < 0) {
            hardware::details::logError("Error releasing supplied bufferFd");
        }

        std::vector<android::hardware::GrantorDescriptor> grantors;
        grantors.resize(configureEventFlagWord ? hardware::details::kMinGrantorCountForEvFlagSupport
                                               : hardware::details::kMinGrantorCount);

        size_t memSize[] = {
                sizeof(hardware::details::RingBufferPosition), /* memory to be allocated for read
                                                                  pointer counter */
                sizeof(hardware::details::RingBufferPosition), /* memory to be allocated for write
                                                                  pointer counter */
                kQueueSizeBytes,              /* memory to be allocated for data buffer */
                sizeof(std::atomic<uint32_t>) /* memory to be allocated for EventFlag word */
        };

        for (size_t grantorPos = 0, offset = 0; grantorPos < grantors.size(); grantorPos++) {
            uint32_t grantorFdIndex;
            size_t grantorOffset;
            if (grantorPos == hardware::details::DATAPTRPOS) {
                grantorFdIndex = 1;
                grantorOffset = 0;
            } else {
                grantorFdIndex = 0;
                grantorOffset = offset;
                offset += memSize[grantorPos];
            }
            grantors[grantorPos] = {
                    0 /* grantor flags */, grantorFdIndex,
                    static_cast<uint32_t>(hardware::details::alignToWordBoundary(grantorOffset)),
                    memSize[grantorPos]};
        }

        mDesc = std::unique_ptr<Descriptor>(new (std::nothrow)
                                                    Descriptor(grantors, mqHandle, quantum));
    } else {
        mDesc = std::unique_ptr<Descriptor>(new (std::nothrow) Descriptor(
                kQueueSizeBytes, mqHandle, quantum, configureEventFlagWord));
    }
    if (mDesc == nullptr) {
        native_handle_close(mqHandle);
        native_handle_delete(mqHandle);
        return;
    }
    initMemory(true);
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
MessageQueueBase<MQDescriptorType, T, flavor>::~MessageQueueBase() {
    if (flavor == kSynchronizedReadWrite && mReadPtr != nullptr) {
        unmapGrantorDescr(mReadPtr, hardware::details::READPTRPOS);
    } else if (mReadPtr != nullptr) {
        delete mReadPtr;
    }
    if (mWritePtr != nullptr) {
        unmapGrantorDescr(mWritePtr, hardware::details::WRITEPTRPOS);
    }
    if (mRing != nullptr) {
        unmapGrantorDescr(mRing, hardware::details::DATAPTRPOS);
    }
    if (mEvFlagWord != nullptr) {
        unmapGrantorDescr(mEvFlagWord, hardware::details::EVFLAGWORDPOS);
        android::hardware::EventFlag::deleteEventFlag(&mEventFlag);
    }
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
bool MessageQueueBase<MQDescriptorType, T, flavor>::write(const T* data) {
    return write(data, 1);
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
bool MessageQueueBase<MQDescriptorType, T, flavor>::read(T* data) {
    return read(data, 1);
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
bool MessageQueueBase<MQDescriptorType, T, flavor>::write(const T* data, size_t nMessages) {
    MemTransaction tx;
    return beginWrite(nMessages, &tx) &&
           tx.copyToSized(data, 0 /* startIdx */, nMessages, quantum()) && commitWrite(nMessages);
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
bool MessageQueueBase<MQDescriptorType, T, flavor>::writeBlocking(
        const T* data, size_t count, uint32_t readNotification, uint32_t writeNotification,
        int64_t timeOutNanos, android::hardware::EventFlag* evFlag) {
    static_assert(flavor == kSynchronizedReadWrite,
                  "writeBlocking can only be used with the "
                  "kSynchronizedReadWrite flavor.");
    /*
     * If evFlag is null and the FMQ does not have its own EventFlag object
     * return false;
     * If the flavor is kSynchronizedReadWrite and the readNotification
     * bit mask is zero return false;
     * If the count is greater than queue size, return false
     * to prevent blocking until timeOut.
     */
    if (evFlag == nullptr) {
        evFlag = mEventFlag;
        if (evFlag == nullptr) {
            hardware::details::logError(
                    "writeBlocking failed: called on MessageQueue with no Eventflag"
                    "configured or provided");
            return false;
        }
    }

    if (readNotification == 0 || (count > getQuantumCount())) {
        return false;
    }

    /*
     * There is no need to wait for a readNotification if there is sufficient
     * space to write is already present in the FMQ. The latter would be the case when
     * read operations read more number of messages than write operations write.
     * In other words, a single large read may clear the FMQ after multiple small
     * writes. This would fail to clear a pending readNotification bit since
     * EventFlag bits can only be cleared by a wait() call, however the bit would
     * be correctly cleared by the next writeBlocking() call.
     */

    bool result = write(data, count);
    if (result) {
        if (writeNotification) {
            evFlag->wake(writeNotification);
        }
        return result;
    }

    bool shouldTimeOut = timeOutNanos != 0;
    int64_t prevTimeNanos = shouldTimeOut ? android::elapsedRealtimeNano() : 0;

    while (true) {
        /* It is not required to adjust 'timeOutNanos' if 'shouldTimeOut' is false */
        if (shouldTimeOut) {
            /*
             * The current time and 'prevTimeNanos' are both CLOCK_BOOTTIME clock values(converted
             * to Nanoseconds)
             */
            int64_t currentTimeNs = android::elapsedRealtimeNano();
            /*
             * Decrement 'timeOutNanos' to account for the time taken to complete the last
             * iteration of the while loop.
             */
            timeOutNanos -= currentTimeNs - prevTimeNanos;
            prevTimeNanos = currentTimeNs;

            if (timeOutNanos <= 0) {
                /*
                 * Attempt write in case a context switch happened outside of
                 * evFlag->wait().
                 */
                result = write(data, count);
                break;
            }
        }

        /*
         * wait() will return immediately if there was a pending read
         * notification.
         */
        uint32_t efState = 0;
        status_t status = evFlag->wait(readNotification, &efState, timeOutNanos,
                                       true /* retry on spurious wake */);

        if (status != android::TIMED_OUT && status != android::NO_ERROR) {
            hardware::details::logError("Unexpected error code from EventFlag Wait status " +
                                        std::to_string(status));
            break;
        }

        if (status == android::TIMED_OUT) {
            break;
        }

        /*
         * If there is still insufficient space to write to the FMQ,
         * keep waiting for another readNotification.
         */
        if ((efState & readNotification) && write(data, count)) {
            result = true;
            break;
        }
    }

    if (result && writeNotification != 0) {
        evFlag->wake(writeNotification);
    }

    return result;
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
bool MessageQueueBase<MQDescriptorType, T, flavor>::writeBlocking(const T* data, size_t count,
                                                                  int64_t timeOutNanos) {
    return writeBlocking(data, count, FMQ_NOT_FULL, FMQ_NOT_EMPTY, timeOutNanos);
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
bool MessageQueueBase<MQDescriptorType, T, flavor>::readBlocking(
        T* data, size_t count, uint32_t readNotification, uint32_t writeNotification,
        int64_t timeOutNanos, android::hardware::EventFlag* evFlag) {
    static_assert(flavor == kSynchronizedReadWrite,
                  "readBlocking can only be used with the "
                  "kSynchronizedReadWrite flavor.");

    /*
     * If evFlag is null and the FMQ does not own its own EventFlag object
     * return false;
     * If the writeNotification bit mask is zero return false;
     * If the count is greater than queue size, return false to prevent
     * blocking until timeOut.
     */
    if (evFlag == nullptr) {
        evFlag = mEventFlag;
        if (evFlag == nullptr) {
            hardware::details::logError(
                    "readBlocking failed: called on MessageQueue with no Eventflag"
                    "configured or provided");
            return false;
        }
    }

    if (writeNotification == 0 || count > getQuantumCount()) {
        return false;
    }

    /*
     * There is no need to wait for a write notification if sufficient
     * data to read is already present in the FMQ. This would be the
     * case when read operations read lesser number of messages than
     * a write operation and multiple reads would be required to clear the queue
     * after a single write operation. This check would fail to clear a pending
     * writeNotification bit since EventFlag bits can only be cleared
     * by a wait() call, however the bit would be correctly cleared by the next
     * readBlocking() call.
     */

    bool result = read(data, count);
    if (result) {
        if (readNotification) {
            evFlag->wake(readNotification);
        }
        return result;
    }

    bool shouldTimeOut = timeOutNanos != 0;
    int64_t prevTimeNanos = shouldTimeOut ? android::elapsedRealtimeNano() : 0;

    while (true) {
        /* It is not required to adjust 'timeOutNanos' if 'shouldTimeOut' is false */
        if (shouldTimeOut) {
            /*
             * The current time and 'prevTimeNanos' are both CLOCK_BOOTTIME clock values(converted
             * to Nanoseconds)
             */
            int64_t currentTimeNs = android::elapsedRealtimeNano();
            /*
             * Decrement 'timeOutNanos' to account for the time taken to complete the last
             * iteration of the while loop.
             */
            timeOutNanos -= currentTimeNs - prevTimeNanos;
            prevTimeNanos = currentTimeNs;

            if (timeOutNanos <= 0) {
                /*
                 * Attempt read in case a context switch happened outside of
                 * evFlag->wait().
                 */
                result = read(data, count);
                break;
            }
        }

        /*
         * wait() will return immediately if there was a pending write
         * notification.
         */
        uint32_t efState = 0;
        status_t status = evFlag->wait(writeNotification, &efState, timeOutNanos,
                                       true /* retry on spurious wake */);

        if (status != android::TIMED_OUT && status != android::NO_ERROR) {
            hardware::details::logError("Unexpected error code from EventFlag Wait status " +
                                        std::to_string(status));
            break;
        }

        if (status == android::TIMED_OUT) {
            break;
        }

        /*
         * If the data in FMQ is still insufficient, go back to waiting
         * for another write notification.
         */
        if ((efState & writeNotification) && read(data, count)) {
            result = true;
            break;
        }
    }

    if (result && readNotification != 0) {
        evFlag->wake(readNotification);
    }
    return result;
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
bool MessageQueueBase<MQDescriptorType, T, flavor>::readBlocking(T* data, size_t count,
                                                                 int64_t timeOutNanos) {
    return readBlocking(data, count, FMQ_NOT_FULL, FMQ_NOT_EMPTY, timeOutNanos);
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
inline size_t MessageQueueBase<MQDescriptorType, T, flavor>::quantum() const {
    if constexpr (std::is_same<T, MQErased>::value) {
        return mDesc->getQuantum();
    } else {
        return kQuantumValue<T>;
    }
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
size_t MessageQueueBase<MQDescriptorType, T, flavor>::availableToWriteBytes() const {
    size_t queueSizeBytes = mDesc->getSize();
    size_t availableBytes = availableToReadBytes();
    if (queueSizeBytes < availableBytes) {
        std::string errorMsg =
                "The write or read pointer has become corrupted. Writing to the queue is no "
                "longer possible. Queue size: " +
                std::to_string(queueSizeBytes) + ", available: " + std::to_string(availableBytes);
        hardware::details::logError(errorMsg);
        if (mErrorHandler) {
            mErrorHandler(Error::POINTER_CORRUPTION, std::move(errorMsg));
        }
        return 0;
    }
    return queueSizeBytes - availableBytes;
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
size_t MessageQueueBase<MQDescriptorType, T, flavor>::availableToWrite() const {
    return availableToWriteBytes() / quantum();
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
size_t MessageQueueBase<MQDescriptorType, T, flavor>::availableToRead() const {
    return availableToReadBytes() / quantum();
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
bool MessageQueueBase<MQDescriptorType, T, flavor>::beginWrite(size_t nMessages,
                                                               MemTransaction* result) const {
    /*
     * If nMessages is greater than size of FMQ or in case of the synchronized
     * FMQ flavor, if there is not enough space to write nMessages, then return
     * result with null addresses.
     */
    if ((flavor == kSynchronizedReadWrite && (availableToWrite() < nMessages)) ||
        nMessages > getQuantumCount()) {
        *result = MemTransaction();
        return false;
    }

    auto writePtr = mWritePtr->load(std::memory_order_relaxed);
    if (writePtr % quantum() != 0) {
        std::string errorMsg =
                "The write pointer has become misaligned. Writing to the queue is not possible. "
                "Pointer: " +
                std::to_string(writePtr) + ", quantum: " + std::to_string(quantum());
        hardware::details::logError(errorMsg);
        hardware::details::errorWriteLog(0x534e4554, "184963385");
        if (mErrorHandler) {
            mErrorHandler(Error::POINTER_CORRUPTION, std::move(errorMsg));
        }
        return false;
    }
    size_t writeOffset = writePtr % mDesc->getSize();

    /*
     * From writeOffset, the number of messages that can be written
     * contiguously without wrapping around the ring buffer are calculated.
     */
    size_t contiguousMessages = (mDesc->getSize() - writeOffset) / quantum();

    if (contiguousMessages < nMessages) {
        /*
         * Wrap around is required. Both result.first and result.second are
         * populated.
         */
        *result = MemTransaction(
                MemRegion(reinterpret_cast<T*>(mRing + writeOffset), contiguousMessages),
                MemRegion(reinterpret_cast<T*>(mRing), nMessages - contiguousMessages));
    } else {
        /*
         * A wrap around is not required to write nMessages. Only result.first
         * is populated.
         */
        *result = MemTransaction(MemRegion(reinterpret_cast<T*>(mRing + writeOffset), nMessages),
                                 MemRegion());
    }

    return true;
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
/*
 * Disable integer sanitization since integer overflow here is allowed
 * and legal.
 */
__attribute__((no_sanitize("integer"))) bool
MessageQueueBase<MQDescriptorType, T, flavor>::commitWrite(size_t nMessages) {
    size_t nBytesWritten = nMessages * quantum();
    auto writePtr = mWritePtr->load(std::memory_order_relaxed);
    writePtr += nBytesWritten;
    mWritePtr->store(writePtr, std::memory_order_release);
    /*
     * This method cannot fail now since we are only incrementing the writePtr
     * counter.
     */
    return true;
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
size_t MessageQueueBase<MQDescriptorType, T, flavor>::availableToReadBytes() const {
    /*
     * This method is invoked by implementations of both read() and write() and
     * hence requires a memory_order_acquired load for both mReadPtr and
     * mWritePtr.
     */
    uint64_t writePtr = mWritePtr->load(std::memory_order_acquire);
    uint64_t readPtr = mReadPtr->load(std::memory_order_acquire);
    if (writePtr < readPtr) {
        std::string errorMsg =
                "The write or read pointer has become corrupted. Reading from the queue is no "
                "longer possible. Write pointer: " +
                std::to_string(writePtr) + ", read pointer: " + std::to_string(readPtr);
        hardware::details::logError(errorMsg);
        if (mErrorHandler) {
            mErrorHandler(Error::POINTER_CORRUPTION, std::move(errorMsg));
        }
        return 0;
    }
    return writePtr - readPtr;
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
bool MessageQueueBase<MQDescriptorType, T, flavor>::read(T* data, size_t nMessages) {
    MemTransaction tx;
    return beginRead(nMessages, &tx) &&
           tx.copyFromSized(data, 0 /* startIdx */, nMessages, quantum()) && commitRead(nMessages);
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
/*
 * Disable integer sanitization since integer overflow here is allowed
 * and legal.
 */
__attribute__((no_sanitize("integer"))) bool
MessageQueueBase<MQDescriptorType, T, flavor>::processOverflow(uint64_t readPtr,
                                                               uint64_t writePtr) const {
    if (writePtr - readPtr > mDesc->getSize()) {
        /*
         * Preserved history can be as big as mDesc->getSize() but we expose only half of that.
         * Half of the buffer will be discarded to make space for fast writers and
         * reduce chance of repeated overflows. The other half is available to read.
         */
        size_t historyOffset = getQuantumCount() / 2 * getQuantumSize();
        mReadPtr->store(writePtr - historyOffset, std::memory_order_release);
        hardware::details::logError("Read failed after an overflow. Resetting read pointer.");
        return true;
    }
    return false;
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
/*
 * Disable integer sanitization since integer overflow here is allowed
 * and legal.
 */
__attribute__((no_sanitize("integer"))) bool
MessageQueueBase<MQDescriptorType, T, flavor>::beginRead(size_t nMessages,
                                                         MemTransaction* result) const {
    *result = MemTransaction();
    /*
     * If it is detected that the data in the queue was overwritten
     * due to the reader process being too slow, the read pointer counter
     * is set to the same as the write pointer counter to indicate error
     * and the read returns false;
     * Need acquire/release memory ordering for mWritePtr.
     */
    auto writePtr = mWritePtr->load(std::memory_order_acquire);
    /*
     * A relaxed load is sufficient for mReadPtr since there will be no
     * stores to mReadPtr from a different thread.
     */
    auto readPtr = mReadPtr->load(std::memory_order_relaxed);
    if (writePtr % quantum() != 0 || readPtr % quantum() != 0) {
        hardware::details::logError(
                "The write or read pointer has become misaligned. Reading from the queue is no "
                "longer possible.");
        hardware::details::errorWriteLog(0x534e4554, "184963385");
        return false;
    }

    if (processOverflow(readPtr, writePtr)) {
        return false;
    }

    size_t nBytesDesired = nMessages * quantum();
    /*
     * Return if insufficient data to read in FMQ.
     */
    if (writePtr - readPtr < nBytesDesired) {
        return false;
    }

    size_t readOffset = readPtr % mDesc->getSize();
    /*
     * From readOffset, the number of messages that can be read contiguously
     * without wrapping around the ring buffer are calculated.
     */
    size_t contiguousMessages = (mDesc->getSize() - readOffset) / quantum();

    if (contiguousMessages < nMessages) {
        /*
         * A wrap around is required. Both result.first and result.second
         * are populated.
         */
        *result = MemTransaction(
                MemRegion(reinterpret_cast<T*>(mRing + readOffset), contiguousMessages),
                MemRegion(reinterpret_cast<T*>(mRing), nMessages - contiguousMessages));
    } else {
        /*
         * A wrap around is not required. Only result.first need to be
         * populated.
         */
        *result = MemTransaction(MemRegion(reinterpret_cast<T*>(mRing + readOffset), nMessages),
                                 MemRegion());
    }

    return true;
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
/*
 * Disable integer sanitization since integer overflow here is allowed
 * and legal.
 */
__attribute__((no_sanitize("integer"))) bool
MessageQueueBase<MQDescriptorType, T, flavor>::commitRead(size_t nMessages) {
    // TODO: Use a local copy of readPtr to avoid relazed mReadPtr loads.
    auto readPtr = mReadPtr->load(std::memory_order_relaxed);
    auto writePtr = mWritePtr->load(std::memory_order_acquire);

    /*
     * If the flavor is unsynchronized, it is possible that a write overflow may
     * have occurred between beginRead() and commitRead().
     */
    if (processOverflow(readPtr, writePtr)) {
        return false;
    }

    size_t nBytesRead = nMessages * quantum();
    readPtr += nBytesRead;
    mReadPtr->store(readPtr, std::memory_order_release);
    return true;
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
size_t MessageQueueBase<MQDescriptorType, T, flavor>::getQuantumSize() const {
    return mDesc->getQuantum();
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
size_t MessageQueueBase<MQDescriptorType, T, flavor>::getQuantumCount() const {
    return mDesc->getSize() / mDesc->getQuantum();
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
bool MessageQueueBase<MQDescriptorType, T, flavor>::isValid() const {
    return mRing != nullptr && mReadPtr != nullptr && mWritePtr != nullptr;
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
void* MessageQueueBase<MQDescriptorType, T, flavor>::mapGrantorDescr(uint32_t grantorIdx) {
    const native_handle_t* handle = mDesc->handle();
    const std::vector<android::hardware::GrantorDescriptor> grantors = mDesc->grantors();
    if (handle == nullptr) {
        hardware::details::logError("mDesc->handle is null");
        return nullptr;
    }

    if (grantorIdx >= grantors.size()) {
        hardware::details::logError(std::string("grantorIdx must be less than ") +
                                    std::to_string(grantors.size()));
        return nullptr;
    }

    int fdIndex = grantors[grantorIdx].fdIndex;
    if (fdIndex < 0 || fdIndex >= handle->numFds) {
        hardware::details::logError(
                std::string("fdIndex (" + std::to_string(fdIndex) + ") from grantor (index " +
                            std::to_string(grantorIdx) +
                            ") must be smaller than the number of fds in the handle: " +
                            std::to_string(handle->numFds)));
        return nullptr;
    }

    /*
     * Offset for mmap must be a multiple of kPageSize.
     */
    if (!hardware::details::isAlignedToWordBoundary(grantors[grantorIdx].offset)) {
        hardware::details::logError("Grantor (index " + std::to_string(grantorIdx) +
                                    ") offset needs to be aligned to word boundary but is: " +
                                    std::to_string(grantors[grantorIdx].offset));
        return nullptr;
    }

    /*
     * Expect some grantors to be at least a min size
     */
    for (uint32_t i = 0; i < grantors.size(); i++) {
        switch (i) {
            case hardware::details::READPTRPOS:
                if (grantors[i].extent < sizeof(uint64_t)) return nullptr;
                break;
            case hardware::details::WRITEPTRPOS:
                if (grantors[i].extent < sizeof(uint64_t)) return nullptr;
                break;
            case hardware::details::DATAPTRPOS:
                // We don't expect specific data size
                break;
            case hardware::details::EVFLAGWORDPOS:
                if (grantors[i].extent < sizeof(uint32_t)) return nullptr;
                break;
            default:
                // We don't care about unknown grantors
                break;
        }
    }

    int mapOffset = (grantors[grantorIdx].offset / kPageSize) * kPageSize;
    if (grantors[grantorIdx].extent < 0 || grantors[grantorIdx].extent > INT_MAX - kPageSize) {
        hardware::details::logError(std::string("Grantor (index " + std::to_string(grantorIdx) +
                                                ") extent value is too large or negative: " +
                                                std::to_string(grantors[grantorIdx].extent)));
        return nullptr;
    }
    int mapLength = grantors[grantorIdx].offset - mapOffset + grantors[grantorIdx].extent;

    void* address = mmap(0, mapLength, PROT_READ | PROT_WRITE, MAP_SHARED, handle->data[fdIndex],
                         mapOffset);
    if (address == MAP_FAILED && errno == EPERM && flavor == kUnsynchronizedWrite) {
        // If the supplied memory is read-only, it would fail with EPERM.
        // Try again to mmap read-only for the kUnsynchronizedWrite case.
        // kSynchronizedReadWrite cannot use read-only memory because the
        // read pointer is stored in the shared memory as well.
        address = mmap(0, mapLength, PROT_READ, MAP_SHARED, handle->data[fdIndex], mapOffset);
    }
    if (address == MAP_FAILED) {
        hardware::details::logError(std::string("mmap failed: ") + std::to_string(errno));
        return nullptr;
    }
    return reinterpret_cast<uint8_t*>(address) + (grantors[grantorIdx].offset - mapOffset);
}

template <template <typename, MQFlavor> typename MQDescriptorType, typename T, MQFlavor flavor>
void MessageQueueBase<MQDescriptorType, T, flavor>::unmapGrantorDescr(void* address,
                                                                      uint32_t grantorIdx) {
    auto grantors = mDesc->grantors();
    if ((address == nullptr) || (grantorIdx >= grantors.size())) {
        return;
    }

    int mapOffset = (grantors[grantorIdx].offset / kPageSize) * kPageSize;
    int mapLength = grantors[grantorIdx].offset - mapOffset + grantors[grantorIdx].extent;
    void* baseAddress =
            reinterpret_cast<uint8_t*>(address) - (grantors[grantorIdx].offset - mapOffset);
    if (baseAddress) munmap(baseAddress, mapLength);
}

}  // namespace android
