code:
/* Copyright (c) 2014, 2020 The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __LOC_PLA__
#define __LOC_PLA__

#ifdef __cplusplus
#include <utils/SystemClock.h>
#define uptimeMillis() android::uptimeMillis()
#define elapsedRealtime() android::elapsedRealtime()
#endif

#ifdef __cplusplus
extern "C" {
#endif

#include <cutils/properties.h>
#include <cutils/threads.h>
#include <cutils/sched_policy.h>
#include <cutils/android_filesystem_config.h>
#include <string.h>
#include <stdlib.h>

#define UID_GPS (AID_GPS)
#define GID_GPS (AID_GPS)
#define UID_LOCCLIENT (4021)
#define GID_LOCCLIENT (4021)

#define LOC_PATH_GPS_CONF_STR      "/vendor/etc/gps.conf"
#define LOC_PATH_IZAT_CONF_STR     "/vendor/etc/izat.conf"
#define LOC_PATH_FLP_CONF_STR      "/vendor/etc/flp.conf"
#define LOC_PATH_LOWI_CONF_STR     "/vendor/etc/lowi.conf"
#define LOC_PATH_SAP_CONF_STR      "/vendor/etc/sap.conf"
#define LOC_PATH_APDR_CONF_STR     "/vendor/etc/apdr.conf"
#define LOC_PATH_XTWIFI_CONF_STR   "/vendor/etc/xtwifi.conf"
#define LOC_PATH_QUIPC_CONF_STR    "/vendor/etc/quipc.conf"
#define LOC_PATH_ANT_CORR_STR      "/vendor/etc/gnss_antenna_info.conf"
#define LOC_PATH_SLIM_CONF_STR     "/vendor/etc/slim.conf"
#define LOC_PATH_VPE_CONF_STR      "/vendor/etc/vpeglue.conf"

/*!
 * @brief Function for memory block copy
 *
 * @param[out] p_Dest     Destination buffer.
 * @param[in]  q_DestSize Destination buffer size.
 * @param[in]  p_Src      Source buffer.
 * @param[in]  q_SrcSize  Source buffer size.
 *
 * @return Number of bytes copied.
 */
static inline size_t memscpy (void *p_Dest, size_t q_DestSize, const void *p_Src, size_t q_SrcSize)
{
    size_t res = (q_DestSize < q_SrcSize) ? q_DestSize : q_SrcSize;
    if (p_Dest && p_Src && q_DestSize > 0 && q_SrcSize > 0) {
        memcpy(p_Dest, p_Src, res);
    } else {
        res = 0;
    }
    return res;
}

/*API for boot kpi marker prints  */
inline int loc_boot_kpi_marker(const char * pFmt, ...)
{
    return -1;
}

#ifdef __cplusplus
}
#endif /*__cplusplus */

#endif /* __LOC_PLA__ */


code:
/* Copyright (c) 2014, 2020 The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __LOC_PLA__
#define __LOC_PLA__

#ifdef __cplusplus
#ifndef FEATURE_EXTERNAL_AP
#include <utils/SystemClock.h>
#endif /* FEATURE_EXTERNAL_AP */
#include <inttypes.h>
#include <sys/time.h>
#include <time.h>

#if defined(__GNUC__) && defined(__GNUC_PREREQ)
#if __GNUC_PREREQ(6,0)
    #pragma message "GNU C version is above 6"
#else
    #pragma message "GNU C version is less than 6"
    #define NO_UNORDERED_SET_OR_MAP
#endif
#endif

// use set/map instead of unordered_set/unordered_map for
// older GCC versions
#ifdef NO_UNORDERED_SET_OR_MAP
#define unordered_set set
#define unordered_map map
#endif

inline int64_t sysTimeMillis(int clock)
{
    struct timespec ts;
    int64_t time_ms = 0;
    clock_gettime(clock, &ts);
    time_ms += (ts.tv_sec * 1000000000LL);
    time_ms += ts.tv_nsec + 500000LL;
    return time_ms / 1000000LL;
}

inline int64_t uptimeMillis() {
    return sysTimeMillis(CLOCK_MONOTONIC);
}
inline int64_t elapsedRealtime() {
    return sysTimeMillis(CLOCK_BOOTTIME);
}

extern "C" {
#endif

#ifndef FEATURE_EXTERNAL_AP
#include <cutils/properties.h>
#include <cutils/threads.h>
#include <cutils/sched_policy.h>
#else
#define set_sched_policy(a, b)
#endif /* FEATURE_EXTERNAL_AP */
#include <pthread.h>
#include <sys/time.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#define MAX_COMMAND_STR_LEN (255)
#define BOOT_KPI_FILE "/sys/kernel/debug/bootkpi/kpi_values"
#ifndef OFF_TARGET
#include <glib.h>
#define strlcat g_strlcat
#define strlcpy g_strlcpy
#else
#define strlcat strncat
#define strlcpy strncpy
#endif

#define UID_GPS (1021)
#define GID_GPS (1021)
#define UID_LOCCLIENT (4021)
#define GID_LOCCLIENT (4021)

#define LOC_PATH_GPS_CONF_STR      "/etc/gps.conf"
#define LOC_PATH_IZAT_CONF_STR     "/etc/izat.conf"
#define LOC_PATH_FLP_CONF_STR      "/etc/flp.conf"
#define LOC_PATH_LOWI_CONF_STR     "/etc/lowi.conf"
#define LOC_PATH_SAP_CONF_STR      "/etc/sap.conf"
#define LOC_PATH_APDR_CONF_STR     "/etc/apdr.conf"
#define LOC_PATH_XTWIFI_CONF_STR   "/etc/xtwifi.conf"
#define LOC_PATH_QUIPC_CONF_STR    "/etc/quipc.conf"
#define LOC_PATH_ANT_CORR_STR      "/etc/gnss_antenna_info.conf"
#define LOC_PATH_SLIM_CONF_STR     "/etc/slim.conf"
#define LOC_PATH_VPE_CONF_STR      "/etc/vpeglue.conf"

#ifdef FEATURE_EXTERNAL_AP
#define PROPERTY_VALUE_MAX 92

inline int property_get(const char* key, char* value, const char* default_value)
{
    strlcpy(value, default_value, PROPERTY_VALUE_MAX - 1);
    return strlen(value);
}
#endif /* FEATURE_EXTERNAL_AP */

/*!
 * @brief Function for memory block copy
 *
 * @param[out] p_Dest     Destination buffer.
 * @param[in]  q_DestSize Destination buffer size.
 * @param[in]  p_Src      Source buffer.
 * @param[in]  q_SrcSize  Source buffer size.
 *
 * @return Number of bytes copied.
 */
static inline size_t memscpy (void *p_Dest, size_t q_DestSize, const void *p_Src, size_t q_SrcSize)
{
    size_t res = (q_DestSize < q_SrcSize) ? q_DestSize : q_SrcSize;
    if (p_Dest && p_Src && q_DestSize > 0 && q_SrcSize > 0) {
        memcpy(p_Dest, p_Src, res);
    } else {
        res = 0;
    }
    return res;
}

/*API for boot kpi marker prints  */
static inline int loc_boot_kpi_marker(const char * pFmt, ...)
{
    int result = 0;
    FILE *stream = NULL;
    char data[MAX_COMMAND_STR_LEN] = {};
    char buf[MAX_COMMAND_STR_LEN] = {};

    va_list ap;
    va_start(ap, pFmt);
    vsnprintf(&buf[0], sizeof(buf), pFmt, ap);
    snprintf(data, sizeof(data), "echo -n %s > %s", buf, BOOT_KPI_FILE);
    stream = popen(data, "w" );
    if (NULL == stream) {
        result = -1;
    } else {
        pclose(stream);
    }
    va_end(ap);
    return result;
}

#ifdef __cplusplus
}
#endif /*__cplusplus */

#endif /* __LOC_PLA__ */

license: unknown


code:
cc_library_shared {

    name: "libgps.utils",
    vendor: true,

    //# Libs
    shared_libs: [
        "libdl",
        "libutils",
        "libcutils",
        "liblog",
        "libprocessgroup",
    ],

    srcs: [
        "loc_log.cpp",
        "loc_cfg.cpp",
        "msg_q.c",
        "linked_list.c",
        "loc_target.cpp",
        "LocHeap.cpp",
        "LocTimer.cpp",
        "LocThread.cpp",
        "MsgTask.cpp",
        "loc_misc_utils.cpp",
        "loc_nmea.cpp",
        "LocIpc.cpp",
        "LogBuffer.cpp",
    ],

    cflags: [
        "-fno-short-enums",
        "-D_ANDROID_",
    ] + GNSS_CFLAGS,

    //# Includes
    ldflags: ["-Wl,--export-dynamic"],

    header_libs: [
        "libutils_headers",
        "libloc_pla_headers",
        "liblocation_api_headers",
    ],
}

cc_library_headers {

    name: "libgps.utils_headers",
    export_include_dirs: ["."],
    vendor: true,
}


code:
/* Copyright (c) 2015, 2020 The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation, nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include <LocHeap.h>

namespace loc_util {

class LocHeapNode {
    friend class LocHeap;

    // size of of the subtree, excluding self, 1 if no subtree
    int mSize;
    LocHeapNode* mLeft;
    LocHeapNode* mRight;
    LocRankable* mData;
public:
    inline LocHeapNode(LocRankable& data) :
        mSize(1), mLeft(NULL), mRight(NULL), mData(&data) {}
    ~LocHeapNode();

    // this only swaps the data of the two nodes, so no
    // detach / re-attached is necessary
    void swap(LocHeapNode& node);

    LocRankable* detachData();

    // push a node into the tree stucture, keeping sorted by rank
    void push(LocHeapNode& node);

    // pop the head node out of the tree stucture. keeping sorted by rank
    static LocHeapNode* pop(LocHeapNode*& top);

    // remove a specific node from the tree
    // returns the pointer to the node removed, which would be either the
    //         same as input (if successfully removed); or NULL (if failed).
    static LocHeapNode* remove(LocHeapNode*& top, LocRankable& data);

    // convenience method to compare data ranking
    inline bool outRanks(LocHeapNode& node) { return mData->outRanks(*node.mData); }
    inline bool outRanks(LocRankable& data) { return mData->outRanks(data); }

    // checks if mSize is correct, AND this node is the highest ranking
    // of the entire subtree
    bool checkNodes();

    inline int getSize() { return mSize; }
};

inline
LocHeapNode::~LocHeapNode() {
    if (mLeft) {
        delete mLeft;
        mLeft = NULL;
    }
    if (mRight) {
        delete mRight;
        mRight = NULL;
    }
    if (mData) {
        mData = NULL;
    }
}

inline
void LocHeapNode::swap(LocHeapNode& node) {
    LocRankable* tmpData = node.mData;
    node.mData = mData;
    mData = tmpData;
}

inline
LocRankable* LocHeapNode::detachData()  {
    LocRankable* data = mData;
    mData = NULL;
    return data;
}

// push keeps the tree sorted by rank, it also tries to balance the
// tree by adding the new node to the smaller of the subtrees.
// The pointer to the tree and internal links never change. If the
// mData of tree top ranks lower than that of the incoming node,
// mData will be swapped with that of the incoming node to ensure
// ranking, no restructuring the container nodes.
void LocHeapNode::push(LocHeapNode& node) {
    // ensure the current node ranks higher than in the incoming one
    if (node.outRanks(*this)) {
        swap(node);
    }

    // now drop the new node (ensured lower than *this) into a subtree
    if (NULL == mLeft) {
        mLeft = &node;
    } else if (NULL == mRight) {
        mRight = &node;
    } else if (mLeft->mSize <= mRight->mSize) {
        mLeft->push(node);
    } else {
        mRight->push(node);
    }
    mSize++;
}

// pop keeps the tree sorted by rank, but it does not try to balance
// the tree. It recursively swaps with the higher ranked top of the
// subtrees.
// The return is a popped out node from leaf level, that has the data
// swapped all the way down from the top. The pinter to the tree and
// internal links will not be changed or restructured, except for the
// node that is popped out.
// If the return pointer == this, this the last node in the tree.
LocHeapNode* LocHeapNode::pop(LocHeapNode*& top) {
    // we know the top has the highest ranking at this point, else
    // the tree is broken. This top will be popped out.  But we need
    // a node from the left or right child, whichever ranks higher,
    // to replace the current top. This then will need to be done
    // recursively to the leaf level. So we swap the mData of the
    // current top node all the way down to the leaf level.
    LocHeapNode* poppedNode = top;
    // top is losing a node in its subtree
    top->mSize--;
    if (top->mLeft || top->mRight) {
        // if mLeft is NULL, mRight for sure is NOT NULL, take that;
        // else if mRight is NULL, mLeft for sure is NOT, take that;
        // else we take the address of whatever has higher ranking mData
        LocHeapNode*& subTop = (NULL == top->mLeft) ? top->mRight :
            ((NULL == top->mRight) ? top->mLeft :
             (top->mLeft->outRanks(*(top->mRight)) ? top->mLeft : top->mRight));
        // swap mData, the tree top gets updated with the new data.
        top->swap(*subTop);
        // pop out from the subtree
        poppedNode = pop(subTop);
    } else {
        // if the top has only single node
        // detach the poppedNode from the tree
        // subTop is the reference of ether mLeft or mRight
        // NOT a local stack pointer. so it MUST be NULL'ed here.
        top = NULL;
    }

    return poppedNode;
}

// navigating through the tree and find the node that hass the input
// data. Since this is a heap, we do recursive linear search.
// returns the pointer to the node removed, which would be either the
//         same as input (if successfully removed); or NULL (if failed).
LocHeapNode* LocHeapNode::remove(LocHeapNode*& top, LocRankable& data) {
    LocHeapNode* removedNode = NULL;
    // this is the node, by address
    if (&data == (LocRankable*)(top->mData)) {
        // pop this node out
        removedNode = pop(top);
    } else if (!data.outRanks(*top->mData)) {
        // subtrees might have this node
        if (top->mLeft) {
            removedNode = remove(top->mLeft, data);
        }
        // if we did not find in mLeft, and mRight is not empty
        if (!removedNode && top->mRight) {
            removedNode = remove(top->mRight, data);
        }

        // top lost a node in its subtree
        if (removedNode) {
            top->mSize--;
        }
    }

    return removedNode;
}

// checks if mSize is correct, AND this node is the highest ranking
// of the entire subtree
bool LocHeapNode::checkNodes() {
    // size of the current subtree
    int totalSize = mSize;
    if (mLeft) {
        // check the consistency of left subtree
        if (mLeft->outRanks(*this) || !mLeft->checkNodes()) {
            return false;
        }
        // subtract the size of left subtree (with subtree head)
        totalSize -= mLeft->mSize;
    }

    if (mRight) {
        // check the consistency of right subtree
        if (mRight->outRanks(*this) || !mRight->checkNodes()) {
            return false;
        }
        // subtract the size of right subtree (with subtree head)
        totalSize -= mRight->mSize;
    }

    // for the tree nodes to consistent, totalSize must be 1 now
    return totalSize == 1;
}

LocHeap::~LocHeap() {
    if (mTree) {
        delete mTree;
    }
}

void LocHeap::push(LocRankable& node) {
    LocHeapNode* heapNode = new LocHeapNode(node);
    if (!mTree) {
        mTree = heapNode;
    } else {
        mTree->push(*heapNode);
    }
}

LocRankable* LocHeap::peek() {
    LocRankable* top = NULL;
    if (mTree) {
        top = mTree->mData;
    }
    return top;
}

LocRankable* LocHeap::pop() {
    LocRankable* locNode = NULL;
    if (mTree) {
        // mTree may become NULL after this call
        LocHeapNode* heapNode = LocHeapNode::pop(mTree);
        locNode = heapNode->detachData();
        delete heapNode;
    }
    return locNode;
}

LocRankable* LocHeap::remove(LocRankable& rankable) {
    LocRankable* locNode = NULL;
    if (mTree) {
        // mTree may become NULL after this call
        LocHeapNode* heapNode = LocHeapNode::remove(mTree, rankable);
        if (heapNode) {
            locNode = heapNode->detachData();
            delete heapNode;
        }
    }
    return locNode;
}

} // namespace loc_util

#ifdef __LOC_UNIT_TEST__
bool LocHeap::checkTree() {
    return ((NULL == mTree) || mTree->checkNodes());
}
uint32_t LocHeap::getTreeSize() {
    return (NULL == mTree) ? 0 : mTree->getSize();
}
#endif


code:
/* Copyright (c) 2015, 2020 The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation, nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#ifndef __LOC_HEAP__
#define __LOC_HEAP__

#include <stddef.h>
#include <string.h>

namespace loc_util {

// abstract class to be implemented by client to provide a rankable class
class LocRankable {
public:
    virtual inline ~LocRankable() {}

    // method to rank objects of such type for sorting purposes.
    // The pointer of the input node would be stored in the heap.
    // >0 if ranks higher than the input;
    // ==0 if equally ranks with the input;
    // <0 if ranks lower than the input
    virtual int ranks(LocRankable& rankable) = 0;

    // convenient method to rank objects of such type for sorting purposes.
    inline bool outRanks(LocRankable& rankable) { return ranks(rankable) > 0; }
};

// opaque class to provide service implementation.
class LocHeapNode;

// a heap whose left and right children are not sorted. It is sorted only vertically,
// i.e. parent always ranks higher than children, if they exist. Ranking algorithm is
// implemented in Rankable. The reason that there is no sort between children is to
// help beter balance the tree with lower cost. When a node is pushed to the tree,
// it is guaranteed that the subtree that is smaller gets to have the new node.
class LocHeap {
protected:
    LocHeapNode* mTree;
public:
    inline LocHeap() : mTree(NULL) {}
    ~LocHeap();

    // push keeps the tree sorted by rank, it also tries to balance the
    // tree by adding the new node to the smaller of the subtrees.
    // node is reference to an obj that is managed by client, that client
    //      creates and destroyes. The destroy should happen after the
    //      node is popped out from the heap.
    void push(LocRankable& node);

    // Peeks the node data on tree top, which has currently the highest ranking
    // There is no change the tree structure with this operation
    // Returns NULL if the tree is empty, otherwise pointer to the node data of
    //         the tree top.
    LocRankable* peek();

    // pop keeps the tree sorted by rank, but it does not try to balance
    // the tree.
    // Return - pointer to the node popped out, or NULL if heap is already empty
    LocRankable* pop();

    // navigating through the tree and find the node that ranks the same
    // as the input data, then remove it from the tree. Rank is implemented
    // by rankable obj.
    // returns the pointer to the node removed; or NULL (if failed).
    LocRankable* remove(LocRankable& rankable);

#ifdef __LOC_UNIT_TEST__
    bool checkTree();
    uint32_t getTreeSize();
#endif
};

} // namespace loc_util

#endif //__LOC_HEAP__


code:
/* Copyright (c) 2017-2020 The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation, nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <netinet/in.h>
#include <netdb.h>
#include <loc_misc_utils.h>
#include <log_util.h>
#include <LocIpc.h>
#include <algorithm>

using namespace std;

namespace loc_util {

#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "LocSvc_LocIpc"

#define SOCK_OP_AND_LOG(buf, length, opable, rtv, exe)  \
    if (nullptr == (buf) || 0 == (length)) { \
        LOC_LOGe("Invalid inputs: buf - %p, length - %u", (buf), (length)); \
    } else if (!(opable)) {                                             \
        LOC_LOGe("Invalid object: operable - %d", (opable));            \
    } else { \
        rtv = (exe); \
        if (-1 == rtv) { \
            LOC_LOGw("failed reason: %s", strerror(errno)); \
        } \
    }

const char Sock::MSG_ABORT[] = "LocIpc::Sock::ABORT";
const char Sock::LOC_IPC_HEAD[] = "$MSGLEN$";
ssize_t Sock::send(const void *buf, uint32_t len, int flags, const struct sockaddr *destAddr,
                          socklen_t addrlen) const {
    ssize_t rtv = -1;
    SOCK_OP_AND_LOG(buf, len, isValid(), rtv, sendto(buf, len, flags, destAddr, addrlen));
    return rtv;
}
ssize_t Sock::recv(const LocIpcRecver& recver, const shared_ptr<ILocIpcListener>& dataCb, int flags,
                   struct sockaddr *srcAddr, socklen_t *addrlen, int sid) const {
    ssize_t rtv = -1;
    if (-1 == sid) {
        sid = mSid;
    } // else it sid would be connection based socket id for recv
    SOCK_OP_AND_LOG(dataCb.get(), mMaxTxSize, isValid(), rtv,
                    recvfrom(recver, dataCb, sid, flags, srcAddr, addrlen));
    return rtv;
}
ssize_t Sock::sendto(const void *buf, size_t len, int flags, const struct sockaddr *destAddr,
                     socklen_t addrlen) const {
    ssize_t rtv = -1;
    if (len <= mMaxTxSize) {
        rtv = ::sendto(mSid, buf, len, flags, destAddr, addrlen);
    } else {
        std::string head(LOC_IPC_HEAD + to_string(len));
        rtv = ::sendto(mSid, head.c_str(), head.length(), flags, destAddr, addrlen);
        if (rtv > 0) {
            for (size_t offset = 0; offset < len && rtv > 0; offset += rtv) {
                rtv = ::sendto(mSid, (char*)buf + offset, min(len - offset, (size_t)mMaxTxSize),
                               flags, destAddr, addrlen);
            }
            rtv = (rtv > 0) ? (head.length() + len) : -1;
        }
    }
    return rtv;
}
ssize_t Sock::recvfrom(const LocIpcRecver& recver, const shared_ptr<ILocIpcListener>& dataCb,
                       int sid, int flags, struct sockaddr *srcAddr, socklen_t *addrlen) const  {
    std::string msg(mMaxTxSize, 0);
    ssize_t nBytes = ::recvfrom(sid, (void*)msg.data(), msg.size(), flags, srcAddr, addrlen);
    if (nBytes > 0) {
        if (strncmp(msg.data(), MSG_ABORT, sizeof(MSG_ABORT)) == 0) {
            LOC_LOGi("recvd abort msg.data %s", msg.data());
            nBytes = 0;
        } else if (strncmp(msg.data(), LOC_IPC_HEAD, sizeof(LOC_IPC_HEAD) - 1)) {
            // short message
            msg.resize(nBytes);
            dataCb->onReceive(msg.data(), nBytes, &recver);
        } else {
            // long message
            size_t msgLen = 0;
            sscanf(msg.data() + sizeof(LOC_IPC_HEAD) - 1, "%zu", &msgLen);
            msg.resize(msgLen);
            for (size_t msgLenReceived = 0; (msgLenReceived < msgLen) && (nBytes > 0);
                 msgLenReceived += nBytes) {
                nBytes = ::recvfrom(sid, &(msg[msgLenReceived]), msg.size() - msgLenReceived,
                                    flags, srcAddr, addrlen);
            }
            if (nBytes > 0) {
                nBytes = msgLen;
                dataCb->onReceive(msg.data(), nBytes, &recver);
            }
        }
    }

    return nBytes;
}
ssize_t Sock::sendAbort(int flags, const struct sockaddr *destAddr, socklen_t addrlen) {
    return send(MSG_ABORT, sizeof(MSG_ABORT), flags, destAddr, addrlen);
}

class LocIpcLocalSender : public LocIpcSender {
protected:
    shared_ptr<Sock> mSock;
    struct sockaddr_un mAddr;
    inline virtual bool isOperable() const override { return mSock != nullptr && mSock->isValid(); }
    inline virtual ssize_t send(const uint8_t data[], uint32_t length, int32_t /* msgId */) const {
        return mSock->send(data, length, 0, (struct sockaddr*)&mAddr, sizeof(mAddr));
    }
public:
    inline LocIpcLocalSender(const char* name) : LocIpcSender(),
            mSock(nullptr),
            mAddr({.sun_family = AF_UNIX, {}}) {

        int fd = -1;
        if (nullptr != name) {
            fd = ::socket(AF_UNIX, SOCK_DGRAM, 0);
            if (fd >= 0) {
                timeval timeout;
                timeout.tv_sec = 2;
                timeout.tv_usec = 0;
                setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
            }
        }
        mSock.reset(new Sock(fd));
        if (mSock != nullptr && mSock->isValid()) {
            snprintf(mAddr.sun_path, sizeof(mAddr.sun_path), "%s", name);
        }
    }
};

class LocIpcLocalRecver : public LocIpcLocalSender, public LocIpcRecver {
protected:
    inline virtual ssize_t recv() const override {
        socklen_t size = sizeof(mAddr);
        return mSock->recv(*this, mDataCb, 0, (struct sockaddr*)&mAddr, &size);
    }
public:
    inline LocIpcLocalRecver(const shared_ptr<ILocIpcListener>& listener, const char* name) :
            LocIpcLocalSender(name), LocIpcRecver(listener, *this) {

        if ((unlink(mAddr.sun_path) < 0) && (errno != ENOENT)) {
            LOC_LOGw("unlink socket error. reason:%s", strerror(errno));
        }

        umask(0157);
        if (mSock->isValid() && ::bind(mSock->mSid, (struct sockaddr*)&mAddr, sizeof(mAddr)) < 0) {
            LOC_LOGe("bind socket error. sock fd: %d: %s, reason: %s", mSock->mSid,
                    mAddr.sun_path, strerror(errno));
            mSock->close();
        }
    }
    inline virtual ~LocIpcLocalRecver() { unlink(mAddr.sun_path); }
    inline virtual const char* getName() const override { return mAddr.sun_path; };
    inline virtual void abort() const override {
        if (isSendable()) {
            mSock->sendAbort(0, (struct sockaddr*)&mAddr, sizeof(mAddr));
        }
    }
};

class LocIpcInetSender : public LocIpcSender {
protected:
    int mSockType;
    shared_ptr<Sock> mSock;
    const string mName;
    sockaddr_in mAddr;
    inline virtual bool isOperable() const override { return mSock != nullptr && mSock->isValid(); }
    virtual ssize_t send(const uint8_t data[], uint32_t length, int32_t /* msgId */) const {
        return mSock->send(data, length, 0, (struct sockaddr*)&mAddr, sizeof(mAddr));
    }
public:
    inline LocIpcInetSender(const LocIpcInetSender& sender) :
            mSockType(sender.mSockType), mSock(sender.mSock),
            mName(sender.mName), mAddr(sender.mAddr) {
    }
    inline LocIpcInetSender(const char* name, int32_t port, int sockType) : LocIpcSender(),
            mSockType(sockType),
            mSock(make_shared<Sock>((nullptr == name) ? -1 : (::socket(AF_INET, mSockType, 0)))),
            mName((nullptr == name) ? "" : name),
            mAddr({.sin_family = AF_INET, .sin_port = htons(port),
                    .sin_addr = {htonl(INADDR_ANY)}}) {
        if (mSock != nullptr && mSock->isValid() && nullptr != name) {
            struct hostent* hp = gethostbyname(name);
            if (nullptr != hp) {
                memcpy((char*)&(mAddr.sin_addr.s_addr), hp->h_addr_list[0], hp->h_length);
            }
        }
    }

    unique_ptr<LocIpcRecver> getRecver(const shared_ptr<ILocIpcListener>& listener) override {
        return make_unique<SockRecver>(listener, *this, mSock);
    }
};

class LocIpcInetTcpSender : public LocIpcInetSender {
protected:
    mutable bool mFirstTime;

    virtual ssize_t send(const uint8_t data[], uint32_t length, int32_t /* msgId */) const {
        if (mFirstTime) {
            mFirstTime = false;
            ::connect(mSock->mSid, (const struct sockaddr*)&mAddr, sizeof(mAddr));
        }
        return mSock->send(data, length, 0, (struct sockaddr*)&mAddr, sizeof(mAddr));
    }

public:
    inline LocIpcInetTcpSender(const char* name, int32_t port) :
            LocIpcInetSender(name, port, SOCK_STREAM),
            mFirstTime(true) {}
};

class LocIpcInetRecver : public LocIpcInetSender, public LocIpcRecver {
     int32_t mPort;
protected:
     virtual ssize_t recv() const = 0;
public:
    inline LocIpcInetRecver(const shared_ptr<ILocIpcListener>& listener, const char* name,
                               int32_t port, int sockType) :
            LocIpcInetSender(name, port, sockType), LocIpcRecver(listener, *this),
            mPort(port) {
        if (mSock->isValid() && ::bind(mSock->mSid, (struct sockaddr*)&mAddr, sizeof(mAddr)) < 0) {
            LOC_LOGe("bind socket error. sock fd: %d, reason: %s", mSock->mSid, strerror(errno));
            mSock->close();
        }
    }
    inline virtual ~LocIpcInetRecver() {}
    inline virtual const char* getName() const override { return mName.data(); };
    inline virtual void abort() const override {
        if (isSendable()) {
            sockaddr_in loopBackAddr = {.sin_family = AF_INET, .sin_port = htons(mPort),
                    .sin_addr = {htonl(INADDR_LOOPBACK)}};
            mSock->sendAbort(0, (struct sockaddr*)&loopBackAddr, sizeof(loopBackAddr));
        }
    }
    inline virtual unique_ptr<LocIpcSender> getLastSender() const override {
        return make_unique<LocIpcInetSender>(static_cast<const LocIpcInetSender&>(*this));
    }
};

class LocIpcInetTcpRecver : public LocIpcInetRecver {
    mutable int32_t mConnFd;
protected:
    inline virtual ssize_t recv() const override {
        socklen_t size = sizeof(mAddr);
        if (-1 == mConnFd && mSock->isValid()) {
            if (::listen(mSock->mSid, 3) < 0 ||
                (mConnFd = accept(mSock->mSid, (struct sockaddr*)&mAddr, &size)) < 0) {
                mSock->close();
                mConnFd = -1;
            }
        }
        return mSock->recv(*this, mDataCb, 0, (struct sockaddr*)&mAddr, &size, mConnFd);
    }
public:
    inline LocIpcInetTcpRecver(const shared_ptr<ILocIpcListener>& listener, const char* name,
                               int32_t port) :
            LocIpcInetRecver(listener, name, port, SOCK_STREAM), mConnFd(-1) {}
    inline virtual ~LocIpcInetTcpRecver() { if (-1 != mConnFd) ::close(mConnFd);}
};

class LocIpcInetUdpRecver : public LocIpcInetRecver {
protected:
    inline virtual ssize_t recv() const override {
        socklen_t size = sizeof(mAddr);
        return mSock->recv(*this, mDataCb, 0, (struct sockaddr*)&mAddr, &size);
    }
public:
    inline LocIpcInetUdpRecver(const shared_ptr<ILocIpcListener>& listener, const char* name,
                                int32_t port) :
            LocIpcInetRecver(listener, name, port, SOCK_DGRAM) {}

    inline virtual ~LocIpcInetUdpRecver() {}
};

class LocIpcRunnable : public LocRunnable {
    bool mAbortCalled;
    LocIpc& mLocIpc;
    unique_ptr<LocIpcRecver> mIpcRecver;
public:
    inline LocIpcRunnable(LocIpc& locIpc, unique_ptr<LocIpcRecver>& ipcRecver) :
            mAbortCalled(false),
            mLocIpc(locIpc),
            mIpcRecver(move(ipcRecver)) {}
    inline virtual bool run() override {
        if (mIpcRecver != nullptr) {
            mLocIpc.startBlockingListening(*(mIpcRecver.get()));
            if (!mAbortCalled) {
                LOC_LOGw("startListeningBlocking() returned w/o stopBlockingListening() called");
            }
        }
        // return false so the calling thread exits while loop
        return false;
    }
    inline virtual void interrupt() override {
        mAbortCalled = true;
        if (mIpcRecver != nullptr) {
            mIpcRecver->abort();
        }
    }
};

bool LocIpc::startNonBlockingListening(unique_ptr<LocIpcRecver>& ipcRecver) {
    if (ipcRecver != nullptr && ipcRecver->isRecvable()) {
        std::string threadName("LocIpc-");
        threadName.append(ipcRecver->getName());
        return mThread.start(threadName.c_str(), make_shared<LocIpcRunnable>(*this, ipcRecver));
    } else {
        LOC_LOGe("ipcRecver is null OR ipcRecver->recvable() is fasle");
        return false;
    }
}

bool LocIpc::startBlockingListening(LocIpcRecver& ipcRecver) {
    if (ipcRecver.isRecvable()) {
        // inform that the socket is ready to receive message
        ipcRecver.onListenerReady();
        while (ipcRecver.recvData());
        return true;
    } else  {
        LOC_LOGe("ipcRecver is null OR ipcRecver->recvable() is fasle");
        return false;
    }
}

void LocIpc::stopNonBlockingListening() {
    mThread.stop();
}

void LocIpc::stopBlockingListening(LocIpcRecver& ipcRecver) {
    if (ipcRecver.isRecvable()) {
        ipcRecver.abort();
    }
}

bool LocIpc::send(LocIpcSender& sender, const uint8_t data[], uint32_t length, int32_t msgId) {
    return sender.sendData(data, length, msgId);
}

shared_ptr<LocIpcSender> LocIpc::getLocIpcLocalSender(const char* localSockName) {
    return make_shared<LocIpcLocalSender>(localSockName);
}
unique_ptr<LocIpcRecver> LocIpc::getLocIpcLocalRecver(const shared_ptr<ILocIpcListener>& listener,
                                                      const char* localSockName) {
    return make_unique<LocIpcLocalRecver>(listener, localSockName);
}
static void* sLibQrtrHandle = nullptr;
static const char* sLibQrtrName = "libloc_socket.so";
shared_ptr<LocIpcSender> LocIpc::getLocIpcQrtrSender(int service, int instance) {
    typedef shared_ptr<LocIpcSender> (*creator_t) (int, int);
    static creator_t creator = (creator_t)dlGetSymFromLib(sLibQrtrHandle, sLibQrtrName,
            "_ZN8loc_util22createLocIpcQrtrSenderEii");
    return (nullptr == creator) ? nullptr : creator(service, instance);
}
unique_ptr<LocIpcRecver> LocIpc::getLocIpcQrtrRecver(const shared_ptr<ILocIpcListener>& listener,
                                                     int service, int instance,
                                                     const shared_ptr<LocIpcQrtrWatcher>& watcher) {
    typedef unique_ptr<LocIpcRecver> (*creator_t)(const shared_ptr<ILocIpcListener>&, int, int,
                                                  const shared_ptr<LocIpcQrtrWatcher>& watcher);
    static creator_t creator = (creator_t)dlGetSymFromLib(sLibQrtrHandle, sLibQrtrName,
#ifdef USE_GLIB
            "_ZN8loc_util22createLocIpcQrtrRecverERKSt10shared_ptrINS_15ILocIpcListenerEEiiRKS0_INS_17LocIpcQrtrWatcherEE");
#else
            "_ZN8loc_util22createLocIpcQrtrRecverERKNSt3__110shared_ptrINS_15ILocIpcListenerEEEiiRKNS1_INS_17LocIpcQrtrWatcherEEE");
#endif
    return (nullptr == creator) ? nullptr : creator(listener, service, instance, watcher);
}
shared_ptr<LocIpcSender> LocIpc::getLocIpcInetTcpSender(const char* serverName, int32_t port) {
    return make_shared<LocIpcInetTcpSender>(serverName, port);
}
unique_ptr<LocIpcRecver> LocIpc::getLocIpcInetTcpRecver(const shared_ptr<ILocIpcListener>& listener,
                                                            const char* serverName, int32_t port) {
    return make_unique<LocIpcInetTcpRecver>(listener, serverName, port);
}
shared_ptr<LocIpcSender> LocIpc::getLocIpcInetUdpSender(const char* serverName, int32_t port) {
    return make_shared<LocIpcInetSender>(serverName, port, SOCK_DGRAM);
}
unique_ptr<LocIpcRecver> LocIpc::getLocIpcInetUdpRecver(const shared_ptr<ILocIpcListener>& listener,
                                                             const char* serverName, int32_t port) {
    return make_unique<LocIpcInetUdpRecver>(listener, serverName, port);
}
pair<shared_ptr<LocIpcSender>, unique_ptr<LocIpcRecver>>
        LocIpc::getLocIpcQmiLocServiceSenderRecverPair(const shared_ptr<ILocIpcListener>& listener, int instance) {
    typedef pair<shared_ptr<LocIpcSender>, unique_ptr<LocIpcRecver>> (*creator_t)(const shared_ptr<ILocIpcListener>&, int);
    static void* sLibEmuHandle = nullptr;
    static creator_t creator = (creator_t)dlGetSymFromLib(sLibEmuHandle, "libloc_emu.so",
#ifdef USE_GLIB
            "_ZN13QmiLocService41createLocIpcQmiLocServiceSenderRecverPair"\
            "ERKSt10shared_ptrIN8loc_util15ILocIpcListenerEEi");
#else
            "_ZN13QmiLocService41createLocIpcQmiLocServiceSenderRecverPair"\
            "ERKNSt3__110shared_ptrIN8loc_util15ILocIpcListenerEEEi");
#endif
    return (nullptr == creator) ?
            make_pair<shared_ptr<LocIpcSender>, unique_ptr<LocIpcRecver>>(nullptr, nullptr) :
            creator(listener, instance);
}

}


code:
/* Copyright (c) 2017-2018, 2020 The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation, nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifndef __LOC_IPC__
#define __LOC_IPC__

#include <string>
#include <memory>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unordered_set>
#include <mutex>
#include <LocThread.h>

using namespace std;

namespace loc_util {

class LocIpcRecver;
class LocIpcSender;

class ILocIpcListener {
protected:
    inline virtual ~ILocIpcListener() {}
public:
    // LocIpc client can overwrite this function to get notification
    // when the socket for LocIpc is ready to receive messages.
    inline virtual void onListenerReady() {}
    virtual void onReceive(const char* data, uint32_t len, const LocIpcRecver* recver) = 0;
};

class LocIpcQrtrWatcher {
    const unordered_set<int> mServicesToWatch;
    unordered_set<int> mClientsToWatch;
    mutex mMutex;
    inline bool isInWatch(const unordered_set<int>& idsToWatch, int id) {
        return idsToWatch.find(id) != idsToWatch.end();
    }
protected:
    inline virtual ~LocIpcQrtrWatcher() {}
    inline LocIpcQrtrWatcher(unordered_set<int> servicesToWatch)
            : mServicesToWatch(servicesToWatch) {}
public:
    enum class ServiceStatus { UP, DOWN };
    inline bool isServiceInWatch(int serviceId) {
        return isInWatch(mServicesToWatch, serviceId);
    }
    inline bool isClientInWatch(int nodeId) {
        lock_guard<mutex> lock(mMutex);
        return isInWatch(mClientsToWatch, nodeId);
    }
    inline void addClientToWatch(int nodeId) {
        lock_guard<mutex> lock(mMutex);
        mClientsToWatch.emplace(nodeId);
    }
    virtual void onServiceStatusChange(int sericeId, int instanceId, ServiceStatus status,
                                       const LocIpcSender& sender) = 0;
    inline virtual void onClientGone(int nodeId, int portId) {}
    inline const unordered_set<int>& getServicesToWatch() { return mServicesToWatch; }
};

class LocIpc {
public:
    inline LocIpc() = default;
    inline virtual ~LocIpc() {
        stopNonBlockingListening();
    }

    static shared_ptr<LocIpcSender>
            getLocIpcLocalSender(const char* localSockName);
    static shared_ptr<LocIpcSender>
            getLocIpcInetUdpSender(const char* serverName, int32_t port);
    static shared_ptr<LocIpcSender>
            getLocIpcInetTcpSender(const char* serverName, int32_t port);
    static shared_ptr<LocIpcSender>
            getLocIpcQrtrSender(int service, int instance);

    static unique_ptr<LocIpcRecver>
            getLocIpcLocalRecver(const shared_ptr<ILocIpcListener>& listener,
                                 const char* localSockName);
    static unique_ptr<LocIpcRecver>
            getLocIpcInetUdpRecver(const shared_ptr<ILocIpcListener>& listener,
                                 const char* serverName, int32_t port);
    static unique_ptr<LocIpcRecver>
            getLocIpcInetTcpRecver(const shared_ptr<ILocIpcListener>& listener,
                                   const char* serverName, int32_t port);
    inline static unique_ptr<LocIpcRecver>
            getLocIpcQrtrRecver(const shared_ptr<ILocIpcListener>& listener,
                                int service, int instance) {
        const shared_ptr<LocIpcQrtrWatcher> qrtrWatcher = nullptr;
        return getLocIpcQrtrRecver(listener, service, instance, qrtrWatcher);
    }
    static unique_ptr<LocIpcRecver>
            getLocIpcQrtrRecver(const shared_ptr<ILocIpcListener>& listener,
                                int service, int instance,
                                const shared_ptr<LocIpcQrtrWatcher>& qrtrWatcher);

    static pair<shared_ptr<LocIpcSender>, unique_ptr<LocIpcRecver>>
            getLocIpcQmiLocServiceSenderRecverPair(const shared_ptr<ILocIpcListener>& listener,
                                                   int instance);

    // Listen for new messages in current thread. Calling this funciton will
    // block current thread.
    // The listening can be stopped by calling stopBlockingListening() passing
    // in the same ipcRecver obj handle.
    static bool startBlockingListening(LocIpcRecver& ipcRecver);
    static void stopBlockingListening(LocIpcRecver& ipcRecver);

    // Create a new LocThread and listen for new messages in it.
    // Calling this function will return immediately and won't block current thread.
    // The listening can be stopped by calling stopNonBlockingListening().
    bool startNonBlockingListening(unique_ptr<LocIpcRecver>& ipcRecver);
    void stopNonBlockingListening();

    // Send out a message.
    // Call this function to send a message in argument data to socket in argument name.
    //
    // Argument name contains the name of the target unix socket. data contains the
    // message to be sent out. Convert your message to a string before calling this function.
    // The function will return true on success, and false on failure.
    static bool send(LocIpcSender& sender, const uint8_t data[],
                     uint32_t length, int32_t msgId = -1);

private:
    LocThread mThread;
};

/* this is only when client needs to implement Sender / Recver that are not already provided by
   the factor methods prvoided by LocIpc. */

class LocIpcSender {
protected:
    LocIpcSender() = default;
    virtual bool isOperable() const = 0;
    virtual ssize_t send(const uint8_t data[], uint32_t length, int32_t msgId) const = 0;
public:
    virtual ~LocIpcSender() = default;
    inline bool isSendable() const { return isOperable(); }
    inline bool sendData(const uint8_t data[], uint32_t length, int32_t msgId) const {
        return isSendable() && (send(data, length, msgId) > 0);
    }
    virtual unique_ptr<LocIpcRecver> getRecver(const shared_ptr<ILocIpcListener>& /*listener*/) {
        return nullptr;
    }
    inline virtual void copyDestAddrFrom(const LocIpcSender& otherSender) {}
};

class LocIpcRecver {
    LocIpcSender& mIpcSender;
protected:
    const shared_ptr<ILocIpcListener> mDataCb;
    inline LocIpcRecver(const shared_ptr<ILocIpcListener>& listener, LocIpcSender& sender) :
            mIpcSender(sender), mDataCb(listener) {}
    LocIpcRecver(LocIpcRecver const& recver) = delete;
    LocIpcRecver& operator=(LocIpcRecver const& recver) = delete;
    virtual ssize_t recv() const = 0;
public:
    virtual ~LocIpcRecver() = default;
    inline bool recvData() const { return isRecvable() && (recv() > 0); }
    inline bool isRecvable() const { return mDataCb != nullptr && mIpcSender.isSendable(); }
    virtual void onListenerReady() { if (mDataCb != nullptr) mDataCb->onListenerReady(); }
    inline virtual unique_ptr<LocIpcSender> getLastSender() const {
        return nullptr;
    }
    virtual void abort() const = 0;
    virtual const char* getName() const = 0;
};

class Sock {
    static const char MSG_ABORT[];
    static const char LOC_IPC_HEAD[];
    const uint32_t mMaxTxSize;
    ssize_t sendto(const void *buf, size_t len, int flags, const struct sockaddr *destAddr,
                   socklen_t addrlen) const;
    ssize_t recvfrom(const LocIpcRecver& recver, const shared_ptr<ILocIpcListener>& dataCb,
                     int sid, int flags, struct sockaddr *srcAddr, socklen_t *addrlen) const;
public:
    int mSid;
    inline Sock(int sid, const uint32_t maxTxSize = 8192) : mMaxTxSize(maxTxSize), mSid(sid) {}
    inline ~Sock() { close(); }
    inline bool isValid() const { return -1 != mSid; }
    ssize_t send(const void *buf, uint32_t len, int flags, const struct sockaddr *destAddr,
                 socklen_t addrlen) const;
    ssize_t recv(const LocIpcRecver& recver, const shared_ptr<ILocIpcListener>& dataCb, int flags,
                 struct sockaddr *srcAddr, socklen_t *addrlen, int sid = -1) const;
    ssize_t sendAbort(int flags, const struct sockaddr *destAddr, socklen_t addrlen);
    inline void close() {
        if (isValid()) {
            ::close(mSid);
            mSid = -1;
        }
    }
};

class SockRecver : public LocIpcRecver {
    shared_ptr<Sock> mSock;
protected:
    inline virtual ssize_t recv() const override {
        return mSock->recv(*this, mDataCb, 0, nullptr, nullptr);
    }
public:
    inline SockRecver(const shared_ptr<ILocIpcListener>& listener,
                  LocIpcSender& sender, shared_ptr<Sock> sock) :
            LocIpcRecver(listener, sender), mSock(sock) {
    }
    inline virtual const char* getName() const override {
        return "SockRecver";
    }
    inline virtual void abort() const override {}
};

}

#endif //__LOC_IPC__


code:
/* Copyright (c) 2020 The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation, nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#ifndef LOC_LOGGER_BASE_H
#define LOC_LOGGER_BASE_H

namespace loc_util {
class LocLoggerBase {
public:
    virtual void log() {}
};
}

#endif


code:
/* Copyright (c) 2015, 2020 The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation, nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#ifndef __LOC_SHARED_LOCK__
#define __LOC_SHARED_LOCK__

#include <stddef.h>
#ifndef FEATURE_EXTERNAL_AP
#include <cutils/atomic.h>
#endif /* FEATURE_EXTERNAL_AP */
#include <pthread.h>

#ifdef FEATURE_EXTERNAL_AP
#include <atomic>

inline int32_t android_atomic_inc(volatile int32_t *addr)
{
    volatile std::atomic_int_least32_t* a = (volatile std::atomic_int_least32_t*)addr;
    return std::atomic_fetch_add_explicit(a, 1, std::memory_order_release);
}

inline int32_t android_atomic_dec(volatile int32_t *addr)
{
    volatile std::atomic_int_least32_t* a = (volatile std::atomic_int_least32_t*)addr;
    return std::atomic_fetch_sub_explicit(a, 1, std::memory_order_release);
}
#endif /* FEATURE_EXTERNAL_AP */

namespace loc_util {

// This is a utility created for use cases such that there are more than
// one client who need to share the same lock, but it is not predictable
// which of these clients is to last to go away. This shared lock deletes
// itself when the last client calls its drop() method. To add a cient,
// this share lock's share() method has to be called, so that the obj
// can maintain an accurate client count.
class LocSharedLock {
    volatile int32_t mRef;
    pthread_mutex_t mMutex;
    inline ~LocSharedLock() { pthread_mutex_destroy(&mMutex); }
public:
    // first client to create this LockSharedLock
    inline LocSharedLock() : mRef(1) { pthread_mutex_init(&mMutex, NULL); }
    // following client(s) are to *share()* this lock created by the first client
    inline LocSharedLock* share() { android_atomic_inc(&mRef); return this; }
    // whe a client no longer needs this shared lock, drop() shall be called.
    inline void drop() { if (1 == android_atomic_dec(&mRef)) delete this; }
    // locking the lock to enter critical section
    inline void lock() { pthread_mutex_lock(&mMutex); }
    // unlocking the lock to leave the critical section
    inline void unlock() { pthread_mutex_unlock(&mMutex); }
};

} //namespace loc_util

#endif //__LOC_SHARED_LOCK__


code:
/* Copyright (c) 2015, 2020 The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation, nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#include <sys/prctl.h>
#include <LocThread.h>
#include <string.h>
#include <string>
#include <thread>
#include <loc_pla.h>

using std::weak_ptr;
using std::shared_ptr;
using std::thread;
using std::string;

namespace loc_util {

class LocThreadDelegate {
    static const char defaultThreadName[];
    weak_ptr<LocRunnable> mRunnable;
    thread mThread;
    LocThreadDelegate(const string tName, shared_ptr<LocRunnable> r);
public:
    ~LocThreadDelegate() {
        shared_ptr<LocRunnable> runnable = mRunnable.lock();
        if (nullptr != runnable) {
            runnable->interrupt();
        }
    }
    inline static LocThreadDelegate* create(const char* tName, shared_ptr<LocRunnable> runnable);
};

const char LocThreadDelegate::defaultThreadName[] = "LocThread";

LocThreadDelegate* LocThreadDelegate::create(const char* tName, shared_ptr<LocRunnable> runnable) {
    LocThreadDelegate* threadDelegate = nullptr;

    if (nullptr != runnable) {
        if (!tName) {
            tName = defaultThreadName;
        }

        char lname[16];
        auto nameSize = strlen(tName) + 1;
        int len = std::min(sizeof(lname), nameSize) - 1;
        memcpy(lname, tName, len);
        lname[len] = 0;

        threadDelegate = new LocThreadDelegate(lname, runnable);
    }

    return threadDelegate;
}

LocThreadDelegate::LocThreadDelegate(const string tName, shared_ptr<LocRunnable> runnable) :
        mRunnable(runnable),
        mThread([tName, runnable] {
                prctl(PR_SET_NAME, tName.c_str(), 0, 0, 0);
                runnable->prerun();
                while (runnable->run());
                runnable->postrun();
            }) {

    mThread.detach();
}

bool LocThread::start(const char* tName, shared_ptr<LocRunnable> runnable) {
    bool success = false;
    if (!mThread) {
        mThread = LocThreadDelegate::create(tName, runnable);
        // true only if thread is created successfully
        success = (NULL != mThread);
    }
    return success;
}

void LocThread::stop() {
    if (nullptr != mThread) {
        delete mThread;
        mThread = nullptr;
    }
}

} // loc_util


code:
/* Copyright (c) 2015, 2020 The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation, nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
#ifndef __LOC_THREAD__
#define __LOC_THREAD__

#include <stddef.h>
#include <memory>

using std::shared_ptr;

namespace loc_util {

// abstract class to be implemented by client to provide a runnable class
// which gets scheduled by LocThread
class LocRunnable {
public:
    inline LocRunnable() = default;
    inline virtual ~LocRunnable() = default;

    // The method to be implemented by thread clients
    // and be scheduled by LocThread
    // This method will be repeated called until it returns false; or
    // until thread is stopped.
    virtual bool run() = 0;

    // The method to be run before thread loop (conditionally repeatedly)
    // calls run()
    inline virtual void prerun() {}

    // The method to be run after thread loop (conditionally repeatedly)
    // calls run()
    inline virtual void postrun() {}

    // The method to wake up the potential blocking thread
    // no op if not applicable
    inline virtual void interrupt() = 0;
};

// opaque class to provide service implementation.
class LocThreadDelegate;

// A utility class to create a thread and run LocRunnable
// caller passes in.
class LocThread {
    LocThreadDelegate* mThread;
public:
    inline LocThread() : mThread(NULL) {}
    inline virtual ~LocThread() { stop(); }

    // client starts thread with a runnable, which implements
    // the logics to fun in the created thread context.
    // The thread is always detached.
    // runnable is an obj managed by client. Client creates and
    //          frees it (but must be after stop() is called, or
    //          this LocThread obj is deleted).
    //          The obj will be deleted by LocThread if start()
    //          returns true. Else it is client's responsibility
    //          to delete the object
    // Returns 0 if success; false if failure.
    bool start(const char* threadName, shared_ptr<LocRunnable> runnable);

    void stop();

    // thread status check
    inline bool isRunning() { return NULL != mThread; }
};

} // loc_util
#endif //__LOC_THREAD__


code:
/* Copyright (c) 2015, 2020 The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation, nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <sys/timerfd.h>
#include <sys/epoll.h>
#include <log_util.h>
#include <loc_timer.h>
#include <LocTimer.h>
#include <LocHeap.h>
#include <LocThread.h>
#include <LocSharedLock.h>
#include <MsgTask.h>

#ifdef __HOST_UNIT_TEST__
#define EPOLLWAKEUP 0
#define CLOCK_BOOTTIME CLOCK_MONOTONIC
#define CLOCK_BOOTTIME_ALARM CLOCK_MONOTONIC
#endif

namespace loc_util {

/*
There are implementations of 5 classes in this file:
LocTimer, LocTimerDelegate, LocTimerContainer, LocTimerPollTask, LocTimerWrapper

LocTimer - client front end, interface for client to start / stop timers, also
           to provide a callback.
LocTimerDelegate - an internal timer entity, which also is a LocRankable obj.
                   Its life cycle is different than that of LocTimer. It gets
                   created when LocTimer::start() is called, and gets deleted
                   when it expires or clients calls the hosting LocTimer obj's
                   stop() method. When a LocTimerDelegate obj is ticking, it
                   stays in the corresponding LocTimerContainer. When expired
                   or stopped, the obj is removed from the container. Since it
                   is also a LocRankable obj, and LocTimerContainer also is a
                   heap, its ranks() implementation decides where it is placed
                   in the heap.
LocTimerContainer - core of the timer service. It is a container (derived from
                    LocHeap) for LocTimerDelegate (implements LocRankable) objs.
                    There are 2 of such containers, one for sw timers (or Linux
                    timers) one for hw timers (or Linux alarms). It adds one of
                    each (those that expire the soonest) to kernel via services
                    provided by LocTimerPollTask. All the heap management on the
                    LocTimerDelegate objs are done in the MsgTask context, such
                    that synchronization is ensured.
LocTimerPollTask - is a class that wraps timerfd and epoll POXIS APIs. It also
                   both implements LocRunnalbe with epoll_wait() in the run()
                   method. It is also a LocThread client, so as to loop the run
                   method.
LocTimerWrapper - a LocTimer client itself, to implement the existing C API with
                  APIs, loc_timer_start() and loc_timer_stop().

*/

class LocTimerPollTask;

// This is a multi-functaional class that:
// * extends the LocHeap class for the detection of head update upon add / remove
//   events. When that happens, soonest time out changes, so timerfd needs update.
// * contains the timers, and add / remove them into the heap
// * provides and maps 2 of such containers, one for timers (or  mSwTimers), one
//   for alarms (or mHwTimers);
// * provides a polling thread;
// * provides a MsgTask thread for synchronized add / remove / timer client callback.
class LocTimerContainer : public LocHeap {
    // mutex to synchronize getters of static members
    static pthread_mutex_t mMutex;
    // Container of timers
    static LocTimerContainer* mSwTimers;
    // Container of alarms
    static LocTimerContainer* mHwTimers;
    // Msg task to provider msg Q, sender and reader.
    static MsgTask* mMsgTask;
    // Poll task to provide epoll call and threading to poll.
    static LocTimerPollTask* mPollTask;
    // timer / alarm fd
    int mDevFd;
    // ctor
    LocTimerContainer(bool wakeOnExpire);
    // dtor
    ~LocTimerContainer();
    static MsgTask* getMsgTaskLocked();
    static LocTimerPollTask* getPollTaskLocked();
    // extend LocHeap and pop if the top outRanks input
    LocTimerDelegate* popIfOutRanks(LocTimerDelegate& timer);
    // update the timer POSIX calls with updated soonest timer spec
    void updateSoonestTime(LocTimerDelegate* priorTop);

public:
    // factory method to control the creation of mSwTimers / mHwTimers
    static LocTimerContainer* get(bool wakeOnExpire);

    LocTimerDelegate* getSoonestTimer();
    int getTimerFd();
    // add a timer / alarm obj into the container
    void add(LocTimerDelegate& timer);
    // remove a timer / alarm obj from the container
    void remove(LocTimerDelegate& timer);
    // handling of timer / alarm expiration
    void expire();
};

class TimerRunnable : public LocRunnable {
    const int mFd;
public:
    inline TimerRunnable(const int fd) : mFd(fd) {}
    // The method to be implemented by thread clients
    // and be scheduled by LocThread
    // This method will be repeated called until it returns false; or
    // until thread is stopped.
    virtual bool run() override;

    // The method to wake up the potential blocking thread
    // no op if not applicable
    inline virtual void interrupt() { close(mFd); }
};

// This class implements the polling thread that epolls imer / alarm fds.
// The LocRunnable::run() contains the actual polling.  The other methods
// will be run in the caller's thread context to add / remove timer / alarm
// fds the kernel, while the polling is blocked on epoll_wait() call.
// Since the design is that we have maximally 2 polls, one for all the
// timers; one for all the alarms, we will poll at most on 2 fds.  But it
// is possile that all we have are only timers or alarms at one time, so we
// allow dynamically add / remove fds we poll on. The design decision of
// having 1 fd per container of timer / alarm is such that, we may not need
// to make a system call each time a timer / alarm is added / removed, unless
// that changes the "soonest" time out of that of all the timers / alarms.
class LocTimerPollTask {
    // the epoll fd
    const int mFd;
    // the thread that calls TimerRunnable::run() method, where
    // epoll_wait() is blocking and waiting for events..
    LocThread mThread;
public:
    // ctor
    LocTimerPollTask();
    // dtor
    ~LocTimerPollTask() = default;
    // add a container of timers. Each contain has a unique device fd, i.e.
    // either timer or alarm fd, and a heap of timers / alarms. It is expected
    // that container would have written to the device fd with the soonest
    // time out value in the heap at the time of calling this method. So all
    // this method does is to add the fd of the input container to the poll
    // and also add the pointer of the container to the event data ptr, such
    // when poll_wait wakes up on events, we know who is the owner of the fd.
    void addPoll(LocTimerContainer& timerContainer);
    // remove a fd that is assciated with a container. The expectation is that
    // the atual timer would have been removed from the container.
    void removePoll(LocTimerContainer& timerContainer);
};

// Internal class of timer obj. It gets born when client calls LocTimer::start();
// and gets deleted when client calls LocTimer::stop() or when the it expire()'s.
// This class implements LocRankable::ranks() so that when an obj is added into
// the container (of LocHeap), it gets placed in sorted order.
class LocTimerDelegate : public LocRankable {
    friend class LocTimerContainer;
    friend class LocTimer;
    LocTimer* mClient;
    LocSharedLock* mLock;
    struct timespec mFutureTime;
    LocTimerContainer* mContainer;
    // not a complete obj, just ctor for LocRankable comparisons
    inline LocTimerDelegate(struct timespec& delay)
        : mClient(NULL), mLock(NULL), mFutureTime(delay), mContainer(NULL) {}
    inline ~LocTimerDelegate() { if (mLock) { mLock->drop(); mLock = NULL; } }
public:
    LocTimerDelegate(LocTimer& client, struct timespec& futureTime, LocTimerContainer* container);
    void destroyLocked();
    // LocRankable virtual method
    virtual int ranks(LocRankable& rankable);
    void expire();
    inline struct timespec getFutureTime() { return mFutureTime; }
};

/***************************LocTimerContainer methods***************************/

// Most of these static recources are created on demand. They however are never
// destoyed. The theory is that there are processes that link to this util lib
// but never use timer, then these resources would never need to be created.
// For those processes that do use timer, it will likely also need to every
// once in a while. It might be cheaper keeping them around.
pthread_mutex_t LocTimerContainer::mMutex = PTHREAD_MUTEX_INITIALIZER;
LocTimerContainer* LocTimerContainer::mSwTimers = NULL;
LocTimerContainer* LocTimerContainer::mHwTimers = NULL;
MsgTask* LocTimerContainer::mMsgTask = NULL;
LocTimerPollTask* LocTimerContainer::mPollTask = NULL;

// ctor - initialize timer heaps
// A container for swTimer (timer) is created, when wakeOnExpire is true; or
// HwTimer (alarm), when wakeOnExpire is false.
LocTimerContainer::LocTimerContainer(bool wakeOnExpire) :
    mDevFd(timerfd_create(wakeOnExpire ? CLOCK_BOOTTIME_ALARM : CLOCK_BOOTTIME, 0)) {

    if ((-1 == mDevFd) && (errno == EINVAL)) {
        LOC_LOGW("%s: timerfd_create failure, fallback to CLOCK_MONOTONIC - %s",
            __FUNCTION__, strerror(errno));
        mDevFd = timerfd_create(CLOCK_MONOTONIC, 0);
    }

    if (-1 != mDevFd) {
        // ensure we have the necessary resources created
        LocTimerContainer::getPollTaskLocked();
        LocTimerContainer::getMsgTaskLocked();
    } else {
        LOC_LOGE("%s: timerfd_create failure - %s", __FUNCTION__, strerror(errno));
    }
}

// dtor
// we do not ever destroy the static resources.
inline
LocTimerContainer::~LocTimerContainer() {
    close(mDevFd);
}

LocTimerContainer* LocTimerContainer::get(bool wakeOnExpire) {
    // get the reference of either mHwTimer or mSwTimers per wakeOnExpire
    LocTimerContainer*& container = wakeOnExpire ? mHwTimers : mSwTimers;
    // it is cheap to check pointer first than locking mutext unconditionally
    if (!container) {
        pthread_mutex_lock(&mMutex);
        // let's check one more time to be safe
        if (!container) {
            container = new LocTimerContainer(wakeOnExpire);
            // timerfd_create failure
            if (-1 == container->getTimerFd()) {
                delete container;
                container = NULL;
            }
        }
        pthread_mutex_unlock(&mMutex);
    }
    return container;
}

MsgTask* LocTimerContainer::getMsgTaskLocked() {
    // it is cheap to check pointer first than locking mutext unconditionally
    if (!mMsgTask) {
        mMsgTask = new MsgTask("LocTimerMsgTask");
    }
    return mMsgTask;
}

LocTimerPollTask* LocTimerContainer::getPollTaskLocked() {
    // it is cheap to check pointer first than locking mutext unconditionally
    if (!mPollTask) {
        mPollTask = new LocTimerPollTask();
    }
    return mPollTask;
}

inline
LocTimerDelegate* LocTimerContainer::getSoonestTimer() {
    return (LocTimerDelegate*)(peek());
}

inline
int LocTimerContainer::getTimerFd() {
    return mDevFd;
}

void LocTimerContainer::updateSoonestTime(LocTimerDelegate* priorTop) {
    LocTimerDelegate* curTop = getSoonestTimer();

    // check if top has changed
    if (curTop != priorTop) {
        struct itimerspec delay;
        memset(&delay, 0, sizeof(struct itimerspec));
        bool toSetTime = false;
        // if tree is empty now, we remove poll and disarm timer
        if (!curTop) {
            mPollTask->removePoll(*this);
            // setting the values to disarm timer
            delay.it_value.tv_sec = 0;
            delay.it_value.tv_nsec = 0;
            toSetTime = true;
        } else if (!priorTop || curTop->outRanks(*priorTop)) {
            // do this first to avoid race condition, in case settime is called
            // with too small an interval
            mPollTask->addPoll(*this);
            delay.it_value = curTop->getFutureTime();
            toSetTime = true;
        }
        if (toSetTime) {
            timerfd_settime(getTimerFd(), TFD_TIMER_ABSTIME, &delay, NULL);
        }
    }
}

// all the heap management is done in the MsgTask context.
inline
void LocTimerContainer::add(LocTimerDelegate& timer) {
    struct MsgTimerPush : public LocMsg {
        LocTimerContainer* mTimerContainer;
        LocTimerDelegate* mTimer;
        inline MsgTimerPush(LocTimerContainer& container, LocTimerDelegate& timer) :
            LocMsg(), mTimerContainer(&container), mTimer(&timer) {}
        inline virtual void proc() const {
            LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();
            mTimerContainer->push((LocRankable&)(*mTimer));
            mTimerContainer->updateSoonestTime(priorTop);
        }
    };

    mMsgTask->sendMsg(new MsgTimerPush(*this, timer));
}

// all the heap management is done in the MsgTask context.
void LocTimerContainer::remove(LocTimerDelegate& timer) {
    struct MsgTimerRemove : public LocMsg {
        LocTimerContainer* mTimerContainer;
        LocTimerDelegate* mTimer;
        inline MsgTimerRemove(LocTimerContainer& container, LocTimerDelegate& timer) :
            LocMsg(), mTimerContainer(&container), mTimer(&timer) {}
        inline virtual void proc() const {
            LocTimerDelegate* priorTop = mTimerContainer->getSoonestTimer();

            // update soonest timer only if mTimer is actually removed from
            // mTimerContainer AND mTimer is not priorTop.
            if (priorTop == ((LocHeap*)mTimerContainer)->remove((LocRankable&)*mTimer)) {
                // if passing in NULL, we tell updateSoonestTime to update
                // kernel with the current top timer interval.
                mTimerContainer->updateSoonestTime(NULL);
            }
            // all timers are deleted here, and only here.
            delete mTimer;
        }
    };

    mMsgTask->sendMsg(new MsgTimerRemove(*this, timer));
}

// all the heap management is done in the MsgTask context.
// Upon expire, we check and continuously pop the heap until
// the top node's timeout is in the future.
void LocTimerContainer::expire() {
    struct MsgTimerExpire : public LocMsg {
        LocTimerContainer* mTimerContainer;
        inline MsgTimerExpire(LocTimerContainer& container) :
            LocMsg(), mTimerContainer(&container) {}
        inline virtual void proc() const {
            struct timespec now;
            // get time spec of now
            clock_gettime(CLOCK_BOOTTIME, &now);
            LocTimerDelegate timerOfNow(now);
            // pop everything in the heap that outRanks now, i.e. has time older than now
            // and then call expire() on that timer.
            for (LocTimerDelegate* timer = (LocTimerDelegate*)mTimerContainer->pop();
                 NULL != timer;
                 timer = mTimerContainer->popIfOutRanks(timerOfNow)) {
                // the timer delegate obj will be deleted before the return of this call
                timer->expire();
            }
            mTimerContainer->updateSoonestTime(NULL);
        }
    };

    struct itimerspec delay;
    memset(&delay, 0, sizeof(struct itimerspec));
    timerfd_settime(getTimerFd(), TFD_TIMER_ABSTIME, &delay, NULL);
    mPollTask->removePoll(*this);
    mMsgTask->sendMsg(new MsgTimerExpire(*this));
}

LocTimerDelegate* LocTimerContainer::popIfOutRanks(LocTimerDelegate& timer) {
    LocTimerDelegate* poppedNode = NULL;
    if (mTree && !timer.outRanks(*peek())) {
        poppedNode = (LocTimerDelegate*)(pop());
    }

    return poppedNode;
}

/***************************LocTimerPollTask methods***************************/

inline
LocTimerPollTask::LocTimerPollTask()
    : mFd(epoll_create(2)), mThread() {
    // before a next call returens, a thread will be created. The run() method
    // could already be running in parallel. Also, since each of the objs
    // creates a thread, the container will make sure that there will be only
    // one of such obj for our timer implementation.
    mThread.start("LocTimerPollTask", std::make_shared<TimerRunnable>(mFd));
}

void LocTimerPollTask::addPoll(LocTimerContainer& timerContainer) {
    struct epoll_event ev;
    memset(&ev, 0, sizeof(ev));

    ev.events = EPOLLIN;
    ev.data.fd = timerContainer.getTimerFd();
    // it is important that we set this context pointer with the input
    // timer container this is how we know which container should handle
    // which expiration.
    ev.data.ptr = &timerContainer;

    epoll_ctl(mFd, EPOLL_CTL_ADD, timerContainer.getTimerFd(), &ev);
}

inline
void LocTimerPollTask::removePoll(LocTimerContainer& timerContainer) {
    epoll_ctl(mFd, EPOLL_CTL_DEL, timerContainer.getTimerFd(), NULL);
}

// The polling thread context will call this method. If run() method needs to
// be repetitvely called, it must return true from the previous call.
bool TimerRunnable::run() {
    struct epoll_event ev[2];

    // we have max 2 descriptors to poll from
    int fds = epoll_wait(mFd, ev, 2, -1);

    // we pretty much want to continually poll until the fd is closed
    bool rerun = (fds > 0) || (errno == EINTR);

    if (fds > 0) {
        // we may have 2 events
        for (int i = 0; i < fds; i++) {
            // each fd has a context pointer associated with the right timer container
            LocTimerContainer* container = (LocTimerContainer*)(ev[i].data.ptr);
            if (container) {
                container->expire();
            } else {
                epoll_ctl(mFd, EPOLL_CTL_DEL, ev[i].data.fd, NULL);
            }
        }
    }

    // if rerun is true, we are requesting to be scheduled again
    return rerun;
}

/***************************LocTimerDelegate methods***************************/

inline
LocTimerDelegate::LocTimerDelegate(LocTimer& client,
                                   struct timespec& futureTime,
                                   LocTimerContainer* container)
    : mClient(&client),
      mLock(mClient->mLock->share()),
      mFutureTime(futureTime),
      mContainer(container) {
    // adding the timer into the container
    mContainer->add(*this);
}

inline
void LocTimerDelegate::destroyLocked() {
    // client handle will likely be deleted soon after this
    // method returns. Nulling this handle so that expire()
    // won't call the callback on the dead handle any more.
    mClient = NULL;

    if (mContainer) {
        LocTimerContainer* container = mContainer;
        mContainer = NULL;
        if (container) {
            container->remove(*this);
        }
    } // else we do not do anything. No such *this* can be
      // created and reached here with mContainer ever been
      // a non NULL. So *this* must have reached the if clause
      // once, and we want it reach there only once.
}

int LocTimerDelegate::ranks(LocRankable& rankable) {
    int rank = -1;
    LocTimerDelegate* timer = (LocTimerDelegate*)(&rankable);
    if (timer) {
        // larger time ranks lower!!!
        // IOW, if input obj has bigger tv_sec/tv_nsec, this obj outRanks higher
        rank = timer->mFutureTime.tv_sec - mFutureTime.tv_sec;
        if(0 == rank)
        {
            //rank against tv_nsec for msec accuracy
            rank = (int)(timer->mFutureTime.tv_nsec - mFutureTime.tv_nsec);
        }
    }
    return rank;
}

inline
void LocTimerDelegate::expire() {
    // keeping a copy of client pointer to be safe
    // when timeOutCallback() is called at the end of this
    // method, *this* obj may be already deleted.
    LocTimer* client = mClient;
    // force a stop, which will lead to delete of this obj
    if (client && client->stop()) {
        // calling client callback with a pointer save on the stack
        // only if stop() returns true, i.e. it hasn't been stopped
        // already.
        client->timeOutCallback();
    }
}

/***************************LocTimer methods***************************/
LocTimer::LocTimer() : mTimer(NULL), mLock(new LocSharedLock()) {
}

LocTimer::~LocTimer() {
    stop();
    if (mLock) {
        mLock->drop();
        mLock = NULL;
    }
}

bool LocTimer::start(unsigned int timeOutInMs, bool wakeOnExpire) {
    bool success = false;
    mLock->lock();
    if (!mTimer) {
        struct timespec futureTime;
        clock_gettime(CLOCK_BOOTTIME, &futureTime);
        futureTime.tv_sec += timeOutInMs / 1000;
        futureTime.tv_nsec += (timeOutInMs % 1000) * 1000000;
        if (futureTime.tv_nsec >= 1000000000) {
            futureTime.tv_sec += futureTime.tv_nsec / 1000000000;
            futureTime.tv_nsec %= 1000000000;
        }

        LocTimerContainer* container;
        container = LocTimerContainer::get(wakeOnExpire);
        if (NULL != container) {
            mTimer = new LocTimerDelegate(*this, futureTime, container);
            // if mTimer is non 0, success should be 0; or vice versa
        }
        success = (NULL != mTimer);
    }
    mLock->unlock();
    return success;
}

bool LocTimer::stop() {
    bool success = false;
    mLock->lock();
    if (mTimer) {
        LocTimerDelegate* timer = mTimer;
        mTimer = NULL;
        if (timer) {
            timer->destroyLocked();
            success = true;
        }
    }
    mLock->unlock();
    return success;
}

/***************************LocTimerWrapper methods***************************/
//////////////////////////////////////////////////////////////////////////
// This section below wraps for the C style APIs
//////////////////////////////////////////////////////////////////////////
class LocTimerWrapper : public LocTimer {
    loc_timer_callback mCb;
    void* mCallerData;
    LocTimerWrapper* mMe;
    static pthread_mutex_t mMutex;
    inline ~LocTimerWrapper() { mCb = NULL; mMe = NULL; }
public:
    inline LocTimerWrapper(loc_timer_callback cb, void* callerData) :
        mCb(cb), mCallerData(callerData), mMe(this) {
    }
    void destroy() {
        pthread_mutex_lock(&mMutex);
        if (NULL != mCb && this == mMe) {
            delete this;
        }
        pthread_mutex_unlock(&mMutex);
    }
    virtual void timeOutCallback() {
        loc_timer_callback cb = mCb;
        void* callerData = mCallerData;
        if (cb) {
            cb(callerData, 0);
        }
        destroy();
    }
};

} // namespace loc_util

//////////////////////////////////////////////////////////////////////////
// This section below wraps for the C style APIs
//////////////////////////////////////////////////////////////////////////

using loc_util::LocTimerWrapper;

pthread_mutex_t LocTimerWrapper::mMutex = PTHREAD_MUTEX_INITIALIZER;

void* loc_timer_start(uint64_t msec, loc_timer_callback cb_func,
                      void *caller_data, bool wake_on_expire)
{
    LocTimerWrapper* locTimerWrapper = NULL;

    if (cb_func) {
        locTimerWrapper = new LocTimerWrapper(cb_func, caller_data);

        if (locTimerWrapper) {
            locTimerWrapper->start(msec, wake_on_expire);
        }
    }

    return locTimerWrapper;
}

void loc_timer_stop(void*&  handle)
{
    if (handle) {
        LocTimerWrapper* locTimerWrapper = (LocTimerWrapper*)(handle);
        locTimerWrapper->destroy();
        handle = NULL;
    }
}


code:
/* Copyright (c) 2015, 2020 The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of The Linux Foundation, nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifndef __LOC_TIMER_CPP_H__
#define __LOC_TIMER_CPP_H__

#include <stddef.h>
#include <loc_pla.h>

namespace loc_util {

// opaque class to provide service implementation.
class LocTimerDelegate;
class LocSharedLock;

// LocTimer client must extend this class and implementthe callback.
// start() / stop() methods are to arm / disarm timer.
class LocTimer
{
    LocTimerDelegate* mTimer;
    LocSharedLock* mLock;
    // don't really want mLock to be manipulated by clients, yet LocTimer
    // has to have a reference to the lock so that the delete of LocTimer
    // and LocTimerDelegate can work together on their share resources.
    friend class LocTimerDelegate;

public:
    LocTimer();
    virtual ~LocTimer();

    // timeOutInMs:  timeout delay in ms
    // wakeOnExpire: true if to wake up CPU (if sleeping) upon timer
    //                        expiration and notify the client.
    //               false if to wait until next time CPU wakes up (if
    //                        sleeping) and then notify the client.
    // return:       true on success;
    //               false on failure, e.g. timer is already running.
    bool start(uint32_t timeOutInMs, bool wakeOnExpire);

    // return:       true on success;
    //               false on failure, e.g. timer is not running.
    bool stop();

    //  LocTimer client Should implement this method.
    //  This method is used for timeout calling back to client. This method
    //  should be short enough (eg: send a message to your own thread).
    virtual void timeOutCallback() = 0;
};

} // namespace loc_util

#endif //__LOC_DELAY_H__