/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * A service that exchanges time synchronization information between
 * a master that defines a timeline and clients that follow the timeline.
 */

#define LOG_TAG "common_time"
#include <utils/Log.h>

#include <arpa/inet.h>
#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <linux/if_ether.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <netinet/ip.h>
#include <poll.h>
#include <stdio.h>
#include <sys/eventfd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <common_time/local_clock.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <utils/Timers.h>

#include "common_clock_service.h"
#include "common_time_config_service.h"
#include "common_time_server.h"
#include "common_time_server_packets.h"
#include "clock_recovery.h"
#include "common_clock.h"

#define MAX_INT ((int)0x7FFFFFFF)

namespace android {

const char*    CommonTimeServer::kDefaultMasterElectionAddr = "255.255.255.255";
const uint16_t CommonTimeServer::kDefaultMasterElectionPort = 8886;
const uint64_t CommonTimeServer::kDefaultSyncGroupID = 1;
const uint8_t  CommonTimeServer::kDefaultMasterPriority = 1;
const uint32_t CommonTimeServer::kDefaultMasterAnnounceIntervalMs = 10000;
const uint32_t CommonTimeServer::kDefaultSyncRequestIntervalMs = 1000;
const uint32_t CommonTimeServer::kDefaultPanicThresholdUsec = 50000;
const bool     CommonTimeServer::kDefaultAutoDisable = true;
const int      CommonTimeServer::kSetupRetryTimeoutMs = 30000;
const int64_t  CommonTimeServer::kNoGoodDataPanicThresholdUsec = 600000000ll;
const uint32_t CommonTimeServer::kRTTDiscardPanicThreshMultiplier = 5;

// timeout value representing an infinite timeout
const int CommonTimeServer::kInfiniteTimeout = -1;

/*** Initial state constants ***/

// number of WhoIsMaster attempts sent before giving up
const int CommonTimeServer::kInitial_NumWhoIsMasterRetries = 6;

// timeout used when waiting for a response to a WhoIsMaster request
const int CommonTimeServer::kInitial_WhoIsMasterTimeoutMs = 500;

/*** Client state constants ***/

// number of sync requests that can fail before a client assumes its master
// is dead
const int CommonTimeServer::kClient_NumSyncRequestRetries = 10;

/*** Master state constants ***/

/*** Ronin state constants ***/

// number of WhoIsMaster attempts sent before declaring ourselves master
const int CommonTimeServer::kRonin_NumWhoIsMasterRetries = 20;

// timeout used when waiting for a response to a WhoIsMaster request
const int CommonTimeServer::kRonin_WhoIsMasterTimeoutMs = 500;

/*** WaitForElection state constants ***/

// how long do we wait for an announcement from a master before
// trying another election?
const int CommonTimeServer::kWaitForElection_TimeoutMs = 12500;

CommonTimeServer::CommonTimeServer()
    : Thread(false)
    , mState(ICommonClock::STATE_INITIAL)
    , mClockRecovery(&mLocalClock, &mCommonClock)
    , mSocket(-1)
    , mLastPacketRxLocalTime(0)
    , mTimelineID(ICommonClock::kInvalidTimelineID)
    , mClockSynced(false)
    , mCommonClockHasClients(false)
    , mStateChangeLog("Recent State Change Events", 30)
    , mElectionLog("Recent Master Election Traffic", 30)
    , mBadPktLog("Recent Bad Packet RX Info", 8)
    , mInitial_WhoIsMasterRequestTimeouts(0)
    , mClient_MasterDeviceID(0)
    , mClient_MasterDevicePriority(0)
    , mRonin_WhoIsMasterRequestTimeouts(0) {
    // zero out sync stats
    resetSyncStats();

    // Setup the master election endpoint to use the default.
    struct sockaddr_in* meep =
        reinterpret_cast<struct sockaddr_in*>(&mMasterElectionEP);
    memset(&mMasterElectionEP, 0, sizeof(mMasterElectionEP));
    inet_aton(kDefaultMasterElectionAddr, &meep->sin_addr);
    meep->sin_family = AF_INET;
    meep->sin_port   = htons(kDefaultMasterElectionPort);

    // Zero out the master endpoint.
    memset(&mMasterEP, 0, sizeof(mMasterEP));
    mMasterEPValid    = false;
    mBindIfaceValid   = false;
    setForceLowPriority(false);

    // Set all remaining configuration parameters to their defaults.
    mDeviceID                 = 0;
    mSyncGroupID              = kDefaultSyncGroupID;
    mMasterPriority           = kDefaultMasterPriority;
    mMasterAnnounceIntervalMs = kDefaultMasterAnnounceIntervalMs;
    mSyncRequestIntervalMs    = kDefaultSyncRequestIntervalMs;
    mPanicThresholdUsec       = kDefaultPanicThresholdUsec;
    mAutoDisable              = kDefaultAutoDisable;

    // Create the eventfd we will use to signal our thread to wake up when
    // needed.
    mWakeupThreadFD = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);

    // seed the random number generator (used to generated timeline IDs)
    srand48(static_cast<unsigned int>(systemTime()));
}

CommonTimeServer::~CommonTimeServer() {
    shutdownThread();

    // No need to grab the lock here.  We are in the destructor; if the the user
    // has a thread in any of the APIs while the destructor is being called,
    // there is a threading problem a the application level we cannot reasonably
    // do anything about.
    cleanupSocket_l();

    if (mWakeupThreadFD >= 0) {
        close(mWakeupThreadFD);
        mWakeupThreadFD = -1;
    }
}

bool CommonTimeServer::startServices() {
    // start the ICommonClock service
    mICommonClock = CommonClockService::instantiate(*this);
    if (mICommonClock == NULL)
        return false;

    // start the ICommonTimeConfig service
    mICommonTimeConfig = CommonTimeConfigService::instantiate(*this);
    if (mICommonTimeConfig == NULL)
        return false;

    return true;
}

bool CommonTimeServer::threadLoop() {
    // Register our service interfaces.
    if (!startServices())
        return false;

    // Hold the lock while we are in the main thread loop.  It will release the
    // lock when it blocks, and hold the lock at all other times.
    mLock.lock();
    runStateMachine_l();
    mLock.unlock();

    IPCThreadState::self()->stopProcess();
    return false;
}

bool CommonTimeServer::runStateMachine_l() {
    if (!mLocalClock.initCheck())
        return false;

    if (!mCommonClock.init(mLocalClock.getLocalFreq()))
        return false;

    // Enter the initial state.
    becomeInitial("startup");

    // run the state machine
    while (!exitPending()) {
        struct pollfd pfds[2];
        int rc, timeout;
        int eventCnt = 0;
        int64_t wakeupTime;
        uint32_t t1, t2;
        bool needHandleTimeout = false;

        // We are always interested in our wakeup FD.
        pfds[eventCnt].fd      = mWakeupThreadFD;
        pfds[eventCnt].events  = POLLIN;
        pfds[eventCnt].revents = 0;
        eventCnt++;

        // If we have a valid socket, then we are interested in what it has to
        // say as well.
        if (mSocket >= 0) {
            pfds[eventCnt].fd      = mSocket;
            pfds[eventCnt].events  = POLLIN;
            pfds[eventCnt].revents = 0;
            eventCnt++;
        }

        t1 = static_cast<uint32_t>(mCurTimeout.msecTillTimeout());
        t2 = static_cast<uint32_t>(mClockRecovery.applyRateLimitedSlew());
        timeout = static_cast<int>(t1 < t2 ? t1 : t2);

        // Note, we were holding mLock when this function was called.  We
        // release it only while we are blocking and hold it at all other times.
        mLock.unlock();
        rc          = poll(pfds, eventCnt, timeout);
        wakeupTime  = mLocalClock.getLocalTime();
        mLock.lock();

        // Is it time to shutdown?  If so, don't hesitate... just do it.
        if (exitPending())
            break;

        // Did the poll fail?  This should never happen and is fatal if it does.
        if (rc < 0) {
            ALOGE("%s:%d poll failed", __PRETTY_FUNCTION__, __LINE__);
            return false;
        }

        if (rc == 0) {
            needHandleTimeout = !mCurTimeout.msecTillTimeout();
            if (needHandleTimeout)
                mCurTimeout.setTimeout(kInfiniteTimeout);
        }

        // Were we woken up on purpose?  If so, clear the eventfd with a read.
        if (pfds[0].revents)
            clearPendingWakeupEvents_l();

        // Is out bind address dirty?  If so, clean up our socket (if any).
        // Alternatively, do we have an active socket but should be auto
        // disabled?  If so, release the socket and enter the proper sync state.
        bool droppedSocket = false;
        if (mBindIfaceDirty || ((mSocket >= 0) && shouldAutoDisable())) {
            cleanupSocket_l();
            mBindIfaceDirty = false;
            droppedSocket = true;
        }

        // Do we not have a socket but should have one?  If so, try to set one
        // up.
        if ((mSocket < 0) && mBindIfaceValid && !shouldAutoDisable()) {
            if (setupSocket_l()) {
                // Success!  We are now joining a new network (either coming
                // from no network, or coming from a potentially different
                // network).  Force our priority to be lower so that we defer to
                // any other masters which may already be on the network we are
                // joining.  Later, when we enter either the client or the
                // master state, we will clear this flag and go back to our
                // normal election priority.
                setForceLowPriority(true);
                switch (mState) {
                    // If we were in initial (whether we had a immediately
                    // before this network or not) we want to simply reset the
                    // system and start again.  Forcing a transition from
                    // INITIAL to INITIAL should do the job.
                    case CommonClockService::STATE_INITIAL:
                        becomeInitial("bound interface");
                        break;

                    // If we were in the master state, then either we were the
                    // master in a no-network situation, or we were the master
                    // of a different network and have moved to a new interface.
                    // In either case, immediately transition to Ronin at low
                    // priority.  If there is no one in the network we just
                    // joined, we will become master soon enough.  If there is,
                    // we want to be certain to defer master status to the
                    // existing timeline currently running on the network.
                    //
                    case CommonClockService::STATE_MASTER:
                        becomeRonin("leaving networkless mode");
                        break;

                    // If we were in any other state (CLIENT, RONIN, or
                    // WAIT_FOR_ELECTION) then we must be moving from one
                    // network to another.  We have lost our old master;
                    // transition to RONIN in an attempt to find a new master.
                    // If there are none out there, we will just assume
                    // responsibility for the timeline we used to be a client
                    // of.
                    default:
                        becomeRonin("bound interface");
                        break;
                }
            } else {
                // That's odd... we failed to set up our socket.  This could be
                // due to some transient network change which will work itself
                // out shortly; schedule a retry attempt in the near future.
                mCurTimeout.setTimeout(kSetupRetryTimeoutMs);
            }

            // One way or the other, we don't have any data to process at this
            // point (since we just tried to bulid a new socket).  Loop back
            // around and wait for the next thing to do.
            continue;
        } else if (droppedSocket) {
            // We just lost our socket, and for whatever reason (either no
            // config, or auto disable engaged) we are not supposed to rebuild
            // one at this time.  We are not going to rebuild our socket until
            // something about our config/auto-disabled status changes, so we
            // are basically in network-less mode.  If we are already in either
            // INITIAL or MASTER, just stay there until something changes.  If
            // we are in any other state (CLIENT, RONIN or WAIT_FOR_ELECTION),
            // then transition to either INITIAL or MASTER depending on whether
            // or not our timeline is valid.
            mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG,
                    "Entering networkless mode interface is %s, "
                    "shouldAutoDisable = %s",
                    mBindIfaceValid ? "valid" : "invalid",
                    shouldAutoDisable() ? "true" : "false");
            if ((mState != ICommonClock::STATE_INITIAL) &&
                (mState != ICommonClock::STATE_MASTER)) {
                if (mTimelineID == ICommonClock::kInvalidTimelineID)
                    becomeInitial("network-less mode");
                else
                    becomeMaster("network-less mode");
            }

            continue;
        }

        // Time to handle the timeouts?
        if (needHandleTimeout) {
            if (!handleTimeout())
                ALOGE("handleTimeout failed");
            continue;
        }

        // Does our socket have data for us (assuming we still have one, we
        // may have RXed a packet at the same time as a config change telling us
        // to shut our socket down)?  If so, process its data.
        if ((mSocket >= 0) && (eventCnt > 1) && (pfds[1].revents)) {
            mLastPacketRxLocalTime = wakeupTime;
            if (!handlePacket())
                ALOGE("handlePacket failed");
        }
    }

    cleanupSocket_l();
    return true;
}

void CommonTimeServer::clearPendingWakeupEvents_l() {
    int64_t tmp;
    read(mWakeupThreadFD, &tmp, sizeof(tmp));
}

void CommonTimeServer::wakeupThread_l() {
    int64_t tmp = 1;
    write(mWakeupThreadFD, &tmp, sizeof(tmp));
}

void CommonTimeServer::cleanupSocket_l() {
    if (mSocket >= 0) {
        close(mSocket);
        mSocket = -1;
    }
}

void CommonTimeServer::shutdownThread() {
    // Flag the work thread for shutdown.
    this->requestExit();

    // Signal the thread in case its sleeping.
    mLock.lock();
    wakeupThread_l();
    mLock.unlock();

    // Wait for the thread to exit.
    this->join();
}

bool CommonTimeServer::setupSocket_l() {
    int rc;
    bool ret_val = false;
    struct sockaddr_in* ipv4_addr = NULL;
    char masterElectionEPStr[64];
    const int one = 1;

    // This should never be needed, but if we happened to have an old socket
    // lying around, be sure to not leak it before proceeding.
    cleanupSocket_l();

    // If we don't have a valid endpoint to bind to, then how did we get here in
    // the first place?  Regardless, we know that we are going to fail to bind,
    // so don't even try.
    if (!mBindIfaceValid)
        return false;

    sockaddrToString(mMasterElectionEP, true, masterElectionEPStr,
                     sizeof(masterElectionEPStr));
    mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG,
                        "Building socket :: bind = %s master election = %s",
                        mBindIface.string(), masterElectionEPStr);

    // TODO: add proper support for IPv6.  Right now, we block IPv6 addresses at
    // the configuration interface level.
    if (AF_INET != mMasterElectionEP.ss_family) {
        mStateChangeLog.log(ANDROID_LOG_WARN, LOG_TAG,
                            "TODO: add proper IPv6 support");
        goto bailout;
    }

    // open a UDP socket for the timeline serivce
    mSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (mSocket < 0) {
        mStateChangeLog.log(ANDROID_LOG_ERROR, LOG_TAG,
                            "Failed to create socket (errno = %d)", errno);
        goto bailout;
    }

    // Bind to the selected interface using Linux's spiffy SO_BINDTODEVICE.
    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", mBindIface.string());
    ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = 0;
    rc = setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE,
                    (void *)&ifr, sizeof(ifr));
    if (rc) {
        mStateChangeLog.log(ANDROID_LOG_ERROR, LOG_TAG,
                            "Failed to bind socket at to interface %s "
                            "(errno = %d)", ifr.ifr_name, errno);
        goto bailout;
    }

    // Bind our socket to INADDR_ANY and the master election port.  The
    // interface binding we made using SO_BINDTODEVICE should limit us to
    // traffic only on the interface we are interested in.  We need to bind to
    // INADDR_ANY and the specific master election port in order to be able to
    // receive both unicast traffic and master election multicast traffic with
    // just a single socket.
    struct sockaddr_in bindAddr;
    ipv4_addr = reinterpret_cast<struct sockaddr_in*>(&mMasterElectionEP);
    memcpy(&bindAddr, ipv4_addr, sizeof(bindAddr));
    bindAddr.sin_addr.s_addr = INADDR_ANY;
    rc = bind(mSocket,
              reinterpret_cast<const sockaddr *>(&bindAddr),
              sizeof(bindAddr));
    if (rc) {
        mStateChangeLog.log(ANDROID_LOG_ERROR, LOG_TAG,
                            "Failed to bind socket to port %hu (errno = %d)",
                            ntohs(bindAddr.sin_port), errno);
        goto bailout;
    }

    if (0xE0000000 == (ntohl(ipv4_addr->sin_addr.s_addr) & 0xF0000000)) {
        // If our master election endpoint is a multicast address, be sure to join
        // the multicast group.
        struct ip_mreq mreq;
        mreq.imr_multiaddr = ipv4_addr->sin_addr;
        mreq.imr_interface.s_addr = htonl(INADDR_ANY);
        rc = setsockopt(mSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
                        &mreq, sizeof(mreq));
        if (rc == -1) {
            ALOGE("Failed to join multicast group at %s.  (errno = %d)",
                 masterElectionEPStr, errno);
            goto bailout;
        }

        // disable loopback of multicast packets
        const int zero = 0;
        rc = setsockopt(mSocket, IPPROTO_IP, IP_MULTICAST_LOOP,
                        &zero, sizeof(zero));
        if (rc == -1) {
            mStateChangeLog.log(ANDROID_LOG_ERROR, LOG_TAG,
                                "Failed to disable multicast loopback "
                                "(errno = %d)", errno);
            goto bailout;
        }
    } else
    if (ntohl(ipv4_addr->sin_addr.s_addr) == 0xFFFFFFFF) {
        // If the master election address is the broadcast address, then enable
        // the broadcast socket option
        rc = setsockopt(mSocket, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one));
        if (rc == -1) {
            mStateChangeLog.log(ANDROID_LOG_ERROR, LOG_TAG,
                                "Failed to enable broadcast (errno = %d)",
                                errno);
            goto bailout;
        }
    } else {
        // If the master election address is neither broadcast, nor multicast,
        // then we are misconfigured.  The config API layer should prevent this
        // from ever happening.
        goto bailout;
    }

    // Set the TTL of sent packets to 1.  (Time protocol sync should never leave
    // the local subnet)
    rc = setsockopt(mSocket, IPPROTO_IP, IP_TTL, &one, sizeof(one));
    if (rc == -1) {
        mStateChangeLog.log(ANDROID_LOG_ERROR, LOG_TAG,
                            "Failed to set TTL to %d (errno = %d)", one, errno);
        goto bailout;
    }

    // get the device's unique ID
    if (!assignDeviceID())
        goto bailout;

    ret_val = true;

bailout:
    if (!ret_val)
        cleanupSocket_l();
    return ret_val;
}

// generate a unique device ID that can be used for arbitration
bool CommonTimeServer::assignDeviceID() {
    if (!mBindIfaceValid)
        return false;

    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_addr.sa_family = AF_INET;
    strlcpy(ifr.ifr_name, mBindIface.string(), IFNAMSIZ);

    int rc = ioctl(mSocket, SIOCGIFHWADDR, &ifr);
    if (rc) {
        ALOGE("%s:%d ioctl failed", __PRETTY_FUNCTION__, __LINE__);
        return false;
    }

    if (ifr.ifr_addr.sa_family != ARPHRD_ETHER) {
        ALOGE("%s:%d got non-Ethernet address", __PRETTY_FUNCTION__, __LINE__);
        return false;
    }

    mDeviceID = 0;
    for (int i = 0; i < ETH_ALEN; i++) {
        mDeviceID = (mDeviceID << 8) | ifr.ifr_hwaddr.sa_data[i];
    }

    return true;
}

// generate a new timeline ID
void CommonTimeServer::assignTimelineID() {
    do {
        mTimelineID = (static_cast<uint64_t>(lrand48()) << 32)
                    |  static_cast<uint64_t>(lrand48());
    } while (mTimelineID == ICommonClock::kInvalidTimelineID);
}

// Select a preference between the device IDs of two potential masters.
// Returns true if the first ID wins, or false if the second ID wins.
bool CommonTimeServer::arbitrateMaster(
        uint64_t deviceID1, uint8_t devicePrio1,
        uint64_t deviceID2, uint8_t devicePrio2) {
    return ((devicePrio1 >  devicePrio2) ||
           ((devicePrio1 == devicePrio2) && (deviceID1 > deviceID2)));
}

static void hexDumpToString(const uint8_t* src, size_t src_len,
                            char* dst, size_t dst_len) {
    size_t offset = 0;
    size_t i;

    for (i = 0; (i < src_len) && (offset < dst_len); ++i) {
        int res;
        if (0 == (i % 16)) {
            res = snprintf(dst + offset, dst_len - offset, "\n%04zx :", i);
            if (res < 0)
                break;
            offset += res;
            if (offset >= dst_len)
                break;
        }

        res = snprintf(dst + offset, dst_len - offset, " %02x", src[i]);
        if (res < 0)
            break;
        offset += res;
    }

    dst[dst_len - 1] = 0;
}

bool CommonTimeServer::handlePacket() {
    uint8_t buf[256];
    struct sockaddr_storage srcAddr;
    socklen_t srcAddrLen = sizeof(srcAddr);

    ssize_t recvBytes = recvfrom(
            mSocket, buf, sizeof(buf), 0,
            reinterpret_cast<const sockaddr *>(&srcAddr), &srcAddrLen);

    if (recvBytes < 0) {
        mBadPktLog.log(ANDROID_LOG_ERROR, LOG_TAG,
                       "recvfrom failed (res %d, errno %d)",
                       recvBytes, errno);
        return false;
    }

    UniversalTimeServicePacket pkt;
    if (pkt.deserializePacket(buf, recvBytes, mSyncGroupID) < 0) {
        char hex[256];
        char srcEPStr[64];

        hexDumpToString(buf, static_cast<size_t>(recvBytes), hex, sizeof(hex));
        sockaddrToString(srcAddr, true, srcEPStr, sizeof(srcEPStr));

        mBadPktLog.log("Failed to parse %d byte packet from %s.%s",
                       recvBytes, srcEPStr, hex);
        return false;
    }

    bool result;
    switch (pkt.packetType) {
        case TIME_PACKET_WHO_IS_MASTER_REQUEST:
            result = handleWhoIsMasterRequest(&pkt.p.who_is_master_request,
                                              srcAddr);
            break;

        case TIME_PACKET_WHO_IS_MASTER_RESPONSE:
            result = handleWhoIsMasterResponse(&pkt.p.who_is_master_response,
                                               srcAddr);
            break;

        case TIME_PACKET_SYNC_REQUEST:
            result = handleSyncRequest(&pkt.p.sync_request, srcAddr);
            break;

        case TIME_PACKET_SYNC_RESPONSE:
            result = handleSyncResponse(&pkt.p.sync_response, srcAddr);
            break;

        case TIME_PACKET_MASTER_ANNOUNCEMENT:
            result = handleMasterAnnouncement(&pkt.p.master_announcement,
                                              srcAddr);
            break;

        default: {
            char srcEPStr[64];
            sockaddrToString(srcAddr, true, srcEPStr, sizeof(srcEPStr));

            mBadPktLog.log(ANDROID_LOG_WARN, LOG_TAG,
                           "unknown packet type (%d) from %s",
                           pkt.packetType, srcEPStr);

            result = false;
        } break;
    }

    return result;
}

bool CommonTimeServer::handleTimeout() {
    // If we have no socket, then this must be a timeout to retry socket setup.
    if (mSocket < 0)
        return true;

    switch (mState) {
        case ICommonClock::STATE_INITIAL:
            return handleTimeoutInitial();
        case ICommonClock::STATE_CLIENT:
            return handleTimeoutClient();
        case ICommonClock::STATE_MASTER:
            return handleTimeoutMaster();
        case ICommonClock::STATE_RONIN:
            return handleTimeoutRonin();
        case ICommonClock::STATE_WAIT_FOR_ELECTION:
            return handleTimeoutWaitForElection();
    }

    return false;
}

bool CommonTimeServer::handleTimeoutInitial() {
    if (++mInitial_WhoIsMasterRequestTimeouts ==
            kInitial_NumWhoIsMasterRetries) {
        // none of our attempts to discover a master succeeded, so make
        // this device the master
        return becomeMaster("initial timeout");
    } else {
        // retry the WhoIsMaster request
        return sendWhoIsMasterRequest();
    }
}

bool CommonTimeServer::handleTimeoutClient() {
    if (shouldPanicNotGettingGoodData())
        return becomeInitial("timeout panic, no good data");

    if (mClient_SyncRequestPending) {
        mClient_SyncRequestPending = false;

        if (++mClient_SyncRequestTimeouts < kClient_NumSyncRequestRetries) {
            // a sync request has timed out, so retry
            return sendSyncRequest();
        } else {
            // The master has failed to respond to a sync request for too many
            // times in a row.  Assume the master is dead and start electing
            // a new master.
            return becomeRonin("master not responding");
        }
    } else {
        // initiate the next sync request
        return sendSyncRequest();
    }
}

bool CommonTimeServer::handleTimeoutMaster() {
    // send another announcement from the master
    return sendMasterAnnouncement();
}

bool CommonTimeServer::handleTimeoutRonin() {
    if (++mRonin_WhoIsMasterRequestTimeouts == kRonin_NumWhoIsMasterRetries) {
        // no other master is out there, so we won the election
        return becomeMaster("no better masters detected");
    } else {
        return sendWhoIsMasterRequest();
    }
}

bool CommonTimeServer::handleTimeoutWaitForElection() {
    return becomeRonin("timeout waiting for election conclusion");
}

bool CommonTimeServer::handleWhoIsMasterRequest(
        const WhoIsMasterRequestPacket* request,
        const sockaddr_storage& srcAddr) {
    // Skip our own messages which come back via broadcast loopback.
    if (request->senderDeviceID == mDeviceID)
        return true;

    char srcEPStr[64];
    sockaddrToString(srcAddr, true, srcEPStr, sizeof(srcEPStr));
    mElectionLog.log("RXed WhoIs master request while in state %s.  "
                     "src %s reqTID %016llx ourTID %016llx",
                     stateToString(mState), srcEPStr,
                     request->timelineID, mTimelineID);

    if (mState == ICommonClock::STATE_MASTER) {
        // is this request related to this master's timeline?
        if (request->timelineID != ICommonClock::kInvalidTimelineID &&
            request->timelineID != mTimelineID)
            return true;

        WhoIsMasterResponsePacket pkt;
        pkt.initHeader(mTimelineID, mSyncGroupID);
        pkt.deviceID = mDeviceID;
        pkt.devicePriority = effectivePriority();

        mElectionLog.log("TXing WhoIs master resp to %s while in state %s.  "
                         "ourTID %016llx ourGID %016llx ourDID %016llx "
                         "ourPrio %u",
                         srcEPStr, stateToString(mState),
                         mTimelineID, mSyncGroupID,
                         pkt.deviceID, pkt.devicePriority);

        uint8_t buf[256];
        ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf));
        if (bufSz < 0)
            return false;

        ssize_t sendBytes = sendto(
                mSocket, buf, bufSz, 0,
                reinterpret_cast<const sockaddr *>(&srcAddr),
                sizeof(srcAddr));
        if (sendBytes == -1) {
            ALOGE("%s:%d sendto failed", __PRETTY_FUNCTION__, __LINE__);
            return false;
        }
    } else if (mState == ICommonClock::STATE_RONIN) {
        // if we hear a WhoIsMaster request from another device following
        // the same timeline and that device wins arbitration, then we will stop
        // trying to elect ourselves master and will instead wait for an
        // announcement from the election winner
        if (request->timelineID != mTimelineID)
            return true;

        if (arbitrateMaster(request->senderDeviceID,
                            request->senderDevicePriority,
                            mDeviceID,
                            effectivePriority()))
            return becomeWaitForElection("would lose election");

        return true;
    } else if (mState == ICommonClock::STATE_INITIAL) {
        // If a group of devices booted simultaneously (e.g. after a power
        // outage) and all of them are in the initial state and there is no
        // master, then each device may time out and declare itself master at
        // the same time.  To avoid this, listen for
        // WhoIsMaster(InvalidTimeline) requests from peers.  If we would lose
        // arbitration against that peer, reset our timeout count so that the
        // peer has a chance to become master before we time out.
        if (request->timelineID == ICommonClock::kInvalidTimelineID &&
                arbitrateMaster(request->senderDeviceID,
                                request->senderDevicePriority,
                                mDeviceID,
                                effectivePriority())) {
            mInitial_WhoIsMasterRequestTimeouts = 0;
        }
    }

    return true;
}

bool CommonTimeServer::handleWhoIsMasterResponse(
        const WhoIsMasterResponsePacket* response,
        const sockaddr_storage& srcAddr) {
    // Skip our own messages which come back via broadcast loopback.
    if (response->deviceID == mDeviceID)
        return true;

    char srcEPStr[64];
    sockaddrToString(srcAddr, true, srcEPStr, sizeof(srcEPStr));
    mElectionLog.log("RXed WhoIs master response while in state %s.  "
                     "src %s respTID %016llx respDID %016llx respPrio %u "
                     "ourTID %016llx",
                     stateToString(mState), srcEPStr,
                     response->timelineID,
                     response->deviceID,
                     static_cast<uint32_t>(response->devicePriority),
                     mTimelineID);

    if (mState == ICommonClock::STATE_INITIAL || mState == ICommonClock::STATE_RONIN) {
        return becomeClient(srcAddr,
                            response->deviceID,
                            response->devicePriority,
                            response->timelineID,
                            "heard whois response");
    } else if (mState == ICommonClock::STATE_CLIENT) {
        // if we get multiple responses because there are multiple devices
        // who believe that they are master, then follow the master that
        // wins arbitration
        if (arbitrateMaster(response->deviceID,
                            response->devicePriority,
                            mClient_MasterDeviceID,
                            mClient_MasterDevicePriority)) {
            return becomeClient(srcAddr,
                                response->deviceID,
                                response->devicePriority,
                                response->timelineID,
                                "heard whois response");
        }
    }

    return true;
}

bool CommonTimeServer::handleSyncRequest(const SyncRequestPacket* request,
                                         const sockaddr_storage& srcAddr) {
    SyncResponsePacket pkt;
    pkt.initHeader(mTimelineID, mSyncGroupID);

    if ((mState == ICommonClock::STATE_MASTER) &&
        (mTimelineID == request->timelineID)) {
        int64_t rxLocalTime = mLastPacketRxLocalTime;
        int64_t rxCommonTime;

        // If we are master on an actual network and have actual clients, then
        // we are no longer low priority.
        setForceLowPriority(false);

        if (OK != mCommonClock.localToCommon(rxLocalTime, &rxCommonTime)) {
            return false;
        }

        int64_t txLocalTime = mLocalClock.getLocalTime();;
        int64_t txCommonTime;
        if (OK != mCommonClock.localToCommon(txLocalTime, &txCommonTime)) {
            return false;
        }

        pkt.nak = 0;
        pkt.clientTxLocalTime  = request->clientTxLocalTime;
        pkt.masterRxCommonTime = rxCommonTime;
        pkt.masterTxCommonTime = txCommonTime;
    } else {
        pkt.nak = 1;
        pkt.clientTxLocalTime  = 0;
        pkt.masterRxCommonTime = 0;
        pkt.masterTxCommonTime = 0;
    }

    uint8_t buf[256];
    ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf));
    if (bufSz < 0)
        return false;

    ssize_t sendBytes = sendto(
            mSocket, &buf, bufSz, 0,
            reinterpret_cast<const sockaddr *>(&srcAddr),
            sizeof(srcAddr));
    if (sendBytes == -1) {
        ALOGE("%s:%d sendto failed", __PRETTY_FUNCTION__, __LINE__);
        return false;
    }

    return true;
}

bool CommonTimeServer::handleSyncResponse(
        const SyncResponsePacket* response,
        const sockaddr_storage& srcAddr) {
    if (mState != ICommonClock::STATE_CLIENT)
        return true;

    assert(mMasterEPValid);
    if (!sockaddrMatch(srcAddr, mMasterEP, true)) {
        char srcEP[64], expectedEP[64];
        sockaddrToString(srcAddr, true, srcEP, sizeof(srcEP));
        sockaddrToString(mMasterEP, true, expectedEP, sizeof(expectedEP));
        ALOGI("Dropping sync response from unexpected address."
             " Expected %s Got %s", expectedEP, srcEP);
        return true;
    }

    if (response->nak) {
        // if our master is no longer accepting requests, then we need to find
        // a new master
        return becomeRonin("master NAK'ed");
    }

    mClient_SyncRequestPending = 0;
    mClient_SyncRequestTimeouts = 0;
    mClient_PacketRTTLog.logRX(response->clientTxLocalTime,
                               mLastPacketRxLocalTime);

    bool result;
    if (!(mClient_SyncRespsRXedFromCurMaster++)) {
        // the first request/response exchange between a client and a master
        // may take unusually long due to ARP, so discard it.
        result = true;
    } else {
        int64_t clientTxLocalTime  = response->clientTxLocalTime;
        int64_t clientRxLocalTime  = mLastPacketRxLocalTime;
        int64_t masterTxCommonTime = response->masterTxCommonTime;
        int64_t masterRxCommonTime = response->masterRxCommonTime;

        int64_t rtt       = (clientRxLocalTime - clientTxLocalTime);
        int64_t avgLocal  = (clientTxLocalTime + clientRxLocalTime) >> 1;
        int64_t avgCommon = (masterTxCommonTime + masterRxCommonTime) >> 1;

        // if the RTT of the packet is significantly larger than the panic
        // threshold, we should simply discard it.  Its better to do nothing
        // than to take cues from a packet like that.
        int64_t rttCommon = mCommonClock.localDurationToCommonDuration(rtt);
        if (rttCommon > (static_cast<int64_t>(mPanicThresholdUsec) * 
                         kRTTDiscardPanicThreshMultiplier)) {
            ALOGV("Dropping sync response with RTT of %" PRId64 " uSec", rttCommon);
            mClient_ExpiredSyncRespsRXedFromCurMaster++;
            if (shouldPanicNotGettingGoodData())
                return becomeInitial("RX panic, no good data");
            return true;
        } else {
            result = mClockRecovery.pushDisciplineEvent(avgLocal, avgCommon, rttCommon);
            mClient_LastGoodSyncRX = clientRxLocalTime;

            if (result) {
                // indicate to listeners that we've synced to the common timeline
                notifyClockSync();
            } else {
                ALOGE("Panic!  Observed clock sync error is too high to tolerate,"
                        " resetting state machine and starting over.");
                notifyClockSyncLoss();
                return becomeInitial("panic");
            }
        }
    }

    mCurTimeout.setTimeout(mSyncRequestIntervalMs);
    return result;
}

bool CommonTimeServer::handleMasterAnnouncement(
        const MasterAnnouncementPacket* packet,
        const sockaddr_storage& srcAddr) {
    uint64_t newDeviceID   = packet->deviceID;
    uint8_t  newDevicePrio = packet->devicePriority;
    uint64_t newTimelineID = packet->timelineID;

    // Skip our own messages which come back via broadcast loopback.
    if (newDeviceID == mDeviceID)
        return true;

    char srcEPStr[64];
    sockaddrToString(srcAddr, true, srcEPStr, sizeof(srcEPStr));
    mElectionLog.log("RXed master announcement while in state %s.  "
                     "src %s srcDevID %lld srcPrio %u srcTID %016llx",
                     stateToString(mState), srcEPStr,
                     newDeviceID, static_cast<uint32_t>(newDevicePrio),
                     newTimelineID);

    if (mState == ICommonClock::STATE_INITIAL ||
        mState == ICommonClock::STATE_RONIN ||
        mState == ICommonClock::STATE_WAIT_FOR_ELECTION) {
        // if we aren't currently following a master, then start following
        // this new master
        return becomeClient(srcAddr,
                            newDeviceID,
                            newDevicePrio,
                            newTimelineID,
                            "heard master announcement");
    } else if (mState == ICommonClock::STATE_CLIENT) {
        // if the new master wins arbitration against our current master,
        // then become a client of the new master
        if (arbitrateMaster(newDeviceID,
                            newDevicePrio,
                            mClient_MasterDeviceID,
                            mClient_MasterDevicePriority))
            return becomeClient(srcAddr,
                                newDeviceID,
                                newDevicePrio,
                                newTimelineID,
                                "heard master announcement");
    } else if (mState == ICommonClock::STATE_MASTER) {
        // two masters are competing - if the new one wins arbitration, then
        // cease acting as master
        if (arbitrateMaster(newDeviceID, newDevicePrio,
                            mDeviceID, effectivePriority()))
            return becomeClient(srcAddr, newDeviceID,
                                newDevicePrio, newTimelineID,
                                "heard master announcement");
    }

    return true;
}

bool CommonTimeServer::sendWhoIsMasterRequest() {
    assert(mState == ICommonClock::STATE_INITIAL || mState == ICommonClock::STATE_RONIN);

    // If we have no socket, then we must be in the unconfigured initial state.
    // Don't report any errors, just don't try to send the initial who-is-master
    // query.  Eventually, our network will either become configured, or we will
    // be forced into network-less master mode by higher level code.
    if (mSocket < 0) {
        assert(mState == ICommonClock::STATE_INITIAL);
        return true;
    }

    bool ret = false;
    WhoIsMasterRequestPacket pkt;
    pkt.initHeader(mSyncGroupID);
    pkt.senderDeviceID = mDeviceID;
    pkt.senderDevicePriority = effectivePriority();

    uint8_t buf[256];
    ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf));
    if (bufSz >= 0) {
        char dstEPStr[64];
        sockaddrToString(mMasterElectionEP, true, dstEPStr, sizeof(dstEPStr));
        mElectionLog.log("TXing WhoIs master request to %s while in state %s.  "
                         "ourTID %016llx ourGID %016llx ourDID %016llx "
                         "ourPrio %u",
                         dstEPStr, stateToString(mState),
                         mTimelineID, mSyncGroupID,
                         pkt.senderDeviceID, pkt.senderDevicePriority);

        ssize_t sendBytes = sendto(
                mSocket, buf, bufSz, 0,
                reinterpret_cast<const sockaddr *>(&mMasterElectionEP),
                sizeof(mMasterElectionEP));
        if (sendBytes < 0)
            ALOGE("WhoIsMaster sendto failed (errno %d)", errno);
        ret = true;
    }

    if (mState == ICommonClock::STATE_INITIAL) {
        mCurTimeout.setTimeout(kInitial_WhoIsMasterTimeoutMs);
    } else {
        mCurTimeout.setTimeout(kRonin_WhoIsMasterTimeoutMs);
    }

    return ret;
}

bool CommonTimeServer::sendSyncRequest() {
    // If we are sending sync requests, then we must be in the client state and
    // we must have a socket (when we have no network, we are only supposed to
    // be in INITIAL or MASTER)
    assert(mState == ICommonClock::STATE_CLIENT);
    assert(mSocket >= 0);

    bool ret = false;
    SyncRequestPacket pkt;
    pkt.initHeader(mTimelineID, mSyncGroupID);
    pkt.clientTxLocalTime = mLocalClock.getLocalTime();

    if (!mClient_FirstSyncTX)
        mClient_FirstSyncTX = pkt.clientTxLocalTime;

    mClient_PacketRTTLog.logTX(pkt.clientTxLocalTime);

    uint8_t buf[256];
    ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf));
    if (bufSz >= 0) {
        ssize_t sendBytes = sendto(
                mSocket, buf, bufSz, 0,
                reinterpret_cast<const sockaddr *>(&mMasterEP),
                sizeof(mMasterEP));
        if (sendBytes < 0)
            ALOGE("SyncRequest sendto failed (errno %d)", errno);
        ret = true;
    }

    mClient_SyncsSentToCurMaster++;
    mCurTimeout.setTimeout(mSyncRequestIntervalMs);
    mClient_SyncRequestPending = true;

    return ret;
}

bool CommonTimeServer::sendMasterAnnouncement() {
    bool ret = false;
    assert(mState == ICommonClock::STATE_MASTER);

    // If we are being asked to send a master announcement, but we have no
    // socket, we must be in network-less master mode.  Don't bother to send the
    // announcement, and don't bother to schedule a timeout.  When the network
    // comes up, the work thread will get poked and start the process of
    // figuring out who the current master should be.
    if (mSocket < 0) {
        mCurTimeout.setTimeout(kInfiniteTimeout);
        return true;
    }

    MasterAnnouncementPacket pkt;
    pkt.initHeader(mTimelineID, mSyncGroupID);
    pkt.deviceID = mDeviceID;
    pkt.devicePriority = effectivePriority();

    uint8_t buf[256];
    ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf));
    if (bufSz >= 0) {
        char dstEPStr[64];
        sockaddrToString(mMasterElectionEP, true, dstEPStr, sizeof(dstEPStr));
        mElectionLog.log("TXing Master announcement to %s while in state %s.  "
                         "ourTID %016llx ourGID %016llx ourDID %016llx "
                         "ourPrio %u",
                         dstEPStr, stateToString(mState),
                         mTimelineID, mSyncGroupID,
                         pkt.deviceID, pkt.devicePriority);

        ssize_t sendBytes = sendto(
                mSocket, buf, bufSz, 0,
                reinterpret_cast<const sockaddr *>(&mMasterElectionEP),
                sizeof(mMasterElectionEP));
        if (sendBytes < 0)
            ALOGE("MasterAnnouncement sendto failed (errno %d)", errno);
        ret = true;
    }

    mCurTimeout.setTimeout(mMasterAnnounceIntervalMs);
    return ret;
}

bool CommonTimeServer::becomeClient(const sockaddr_storage& masterEP,
                                    uint64_t masterDeviceID,
                                    uint8_t  masterDevicePriority,
                                    uint64_t timelineID,
                                    const char* cause) {
    char newEPStr[64], oldEPStr[64];
    sockaddrToString(masterEP, true, newEPStr, sizeof(newEPStr));
    sockaddrToString(mMasterEP, mMasterEPValid, oldEPStr, sizeof(oldEPStr));

    mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG,
            "%s --> CLIENT (%s) :%s"
            " OldMaster: %02x-%014llx::%016llx::%s"
            " NewMaster: %02x-%014llx::%016llx::%s",
            stateToString(mState), cause,
            (mTimelineID != timelineID) ? " (new timeline)" : "",
            mClient_MasterDevicePriority, mClient_MasterDeviceID,
            mTimelineID, oldEPStr,
            masterDevicePriority, masterDeviceID,
            timelineID, newEPStr);

    if (mTimelineID != timelineID) {
        // start following a new timeline
        mTimelineID = timelineID;
        mClockRecovery.reset(true, true);
        notifyClockSyncLoss();
    } else {
        // start following a new master on the existing timeline
        mClockRecovery.reset(false, true);
    }

    mMasterEP = masterEP;
    mMasterEPValid = true;

    // If we are on a real network as a client of a real master, then we should
    // no longer force low priority.  If our master disappears, we should have
    // the high priority bit set during the election to replace the master
    // because this group was a real group and not a singleton created in
    // networkless mode.
    setForceLowPriority(false);

    mClient_MasterDeviceID = masterDeviceID;
    mClient_MasterDevicePriority = masterDevicePriority;
    resetSyncStats();

    setState(ICommonClock::STATE_CLIENT);

    // add some jitter to when the various clients send their requests
    // in order to reduce the likelihood that a group of clients overload
    // the master after receiving a master announcement
    usleep((lrand48() % 100) * 1000);

    return sendSyncRequest();
}

bool CommonTimeServer::becomeMaster(const char* cause) {
    uint64_t oldTimelineID = mTimelineID;
    if (mTimelineID == ICommonClock::kInvalidTimelineID) {
        // this device has not been following any existing timeline,
        // so it will create a new timeline and declare itself master
        assert(!mCommonClock.isValid());

        // set the common time basis
        mCommonClock.setBasis(mLocalClock.getLocalTime(), 0);

        // assign an arbitrary timeline iD
        assignTimelineID();

        // notify listeners that we've created a common timeline
        notifyClockSync();
    }

    mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG,
            "%s --> MASTER (%s) : %s timeline %016llx",
            stateToString(mState), cause,
            (oldTimelineID == mTimelineID) ? "taking ownership of"
                                           : "creating new",
            mTimelineID);

    memset(&mMasterEP, 0, sizeof(mMasterEP));
    mMasterEPValid = false;
    mClient_MasterDevicePriority = effectivePriority();
    mClient_MasterDeviceID = mDeviceID;
    mClockRecovery.reset(false, true);
    resetSyncStats();

    setState(ICommonClock::STATE_MASTER);
    return sendMasterAnnouncement();
}

bool CommonTimeServer::becomeRonin(const char* cause) {
    // If we were the client of a given timeline, but had never received even a
    // single time sync packet, then we transition back to Initial instead of
    // Ronin.  If we transition to Ronin and end up becoming the new Master, we
    // will be unable to service requests for other clients because we never
    // actually knew what time it was.  By going to initial, we ensure that
    // other clients who know what time it is, but would lose master arbitration
    // in the Ronin case, will step up and become the proper new master of the
    // old timeline.

    char oldEPStr[64];
    sockaddrToString(mMasterEP, mMasterEPValid, oldEPStr, sizeof(oldEPStr));
    memset(&mMasterEP, 0, sizeof(mMasterEP));
    mMasterEPValid = false;

    if (mCommonClock.isValid()) {
        mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG,
             "%s --> RONIN (%s) : lost track of previously valid timeline "
             "%02x-%014llx::%016llx::%s (%d TXed %d RXed %d RXExpired)",
             stateToString(mState), cause,
             mClient_MasterDevicePriority, mClient_MasterDeviceID,
             mTimelineID, oldEPStr,
             mClient_SyncsSentToCurMaster,
             mClient_SyncRespsRXedFromCurMaster,
             mClient_ExpiredSyncRespsRXedFromCurMaster);

        mRonin_WhoIsMasterRequestTimeouts = 0;
        setState(ICommonClock::STATE_RONIN);
        return sendWhoIsMasterRequest();
    } else {
        mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG,
             "%s --> INITIAL (%s) : never synced timeline "
             "%02x-%014llx::%016llx::%s (%d TXed %d RXed %d RXExpired)",
             stateToString(mState), cause,
             mClient_MasterDevicePriority, mClient_MasterDeviceID,
             mTimelineID, oldEPStr,
             mClient_SyncsSentToCurMaster,
             mClient_SyncRespsRXedFromCurMaster,
             mClient_ExpiredSyncRespsRXedFromCurMaster);

        return becomeInitial("ronin, no timeline");
    }
}

bool CommonTimeServer::becomeWaitForElection(const char* cause) {
    mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG,
         "%s --> WAIT_FOR_ELECTION (%s) : dropping out of election,"
         " waiting %d mSec for completion.",
         stateToString(mState), cause, kWaitForElection_TimeoutMs);

    setState(ICommonClock::STATE_WAIT_FOR_ELECTION);
    mCurTimeout.setTimeout(kWaitForElection_TimeoutMs);
    return true;
}

bool CommonTimeServer::becomeInitial(const char* cause) {
    mStateChangeLog.log(ANDROID_LOG_INFO, LOG_TAG,
                        "Entering INITIAL (%s), total reset.",
                        cause);

    setState(ICommonClock::STATE_INITIAL);

    // reset clock recovery
    mClockRecovery.reset(true, true);

    // reset internal state bookkeeping.
    mCurTimeout.setTimeout(kInfiniteTimeout);
    memset(&mMasterEP, 0, sizeof(mMasterEP));
    mMasterEPValid = false;
    mLastPacketRxLocalTime = 0;
    mTimelineID = ICommonClock::kInvalidTimelineID;
    mClockSynced = false;
    mInitial_WhoIsMasterRequestTimeouts = 0;
    mClient_MasterDeviceID = 0;
    mClient_MasterDevicePriority = 0;
    mRonin_WhoIsMasterRequestTimeouts = 0;
    resetSyncStats();

    // send the first request to discover the master
    return sendWhoIsMasterRequest();
}

void CommonTimeServer::notifyClockSync() {
    if (!mClockSynced) {
        mClockSynced = true;
        mICommonClock->notifyOnTimelineChanged(mTimelineID);
    }
}

void CommonTimeServer::notifyClockSyncLoss() {
    if (mClockSynced) {
        mClockSynced = false;
        mICommonClock->notifyOnTimelineChanged(
                ICommonClock::kInvalidTimelineID);
    }
}

void CommonTimeServer::setState(ICommonClock::State s) {
    mState = s;
}

const char* CommonTimeServer::stateToString(ICommonClock::State s) {
    switch(s) {
        case ICommonClock::STATE_INITIAL:
            return "INITIAL";
        case ICommonClock::STATE_CLIENT:
            return "CLIENT";
        case ICommonClock::STATE_MASTER:
            return "MASTER";
        case ICommonClock::STATE_RONIN:
            return "RONIN";
        case ICommonClock::STATE_WAIT_FOR_ELECTION:
            return "WAIT_FOR_ELECTION";
        default:
            return "unknown";
    }
}

void CommonTimeServer::sockaddrToString(const sockaddr_storage& addr,
                                        bool addrValid,
                                        char* buf, size_t bufLen) {
    if (!bufLen || !buf)
        return;

    if (addrValid) {
        switch (addr.ss_family) {
            case AF_INET: {
                const struct sockaddr_in* sa =
                    reinterpret_cast<const struct sockaddr_in*>(&addr);
                unsigned long a = ntohl(sa->sin_addr.s_addr);
                uint16_t      p = ntohs(sa->sin_port);
                snprintf(buf, bufLen, "%lu.%lu.%lu.%lu:%hu",
                        ((a >> 24) & 0xFF), ((a >> 16) & 0xFF),
                        ((a >>  8) & 0xFF),  (a        & 0xFF), p);
            } break;

            case AF_INET6: {
                const struct sockaddr_in6* sa =
                    reinterpret_cast<const struct sockaddr_in6*>(&addr);
                const uint8_t* a = sa->sin6_addr.s6_addr;
                uint16_t       p = ntohs(sa->sin6_port);
                snprintf(buf, bufLen,
                        "%02X%02X:%02X%02X:%02X%02X:%02X%02X:"
                        "%02X%02X:%02X%02X:%02X%02X:%02X%02X port %hd",
                        a[0], a[1], a[ 2], a[ 3], a[ 4], a[ 5], a[ 6], a[ 7],
                        a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15],
                        p);
            } break;

            default:
                snprintf(buf, bufLen,
                         "<unknown sockaddr family %d>", addr.ss_family);
                break;
        }
    } else {
        snprintf(buf, bufLen, "<none>");
    }

    buf[bufLen - 1] = 0;
}

bool CommonTimeServer::sockaddrMatch(const sockaddr_storage& a1,
                                     const sockaddr_storage& a2,
                                     bool matchAddressOnly) {
    if (a1.ss_family != a2.ss_family)
        return false;

    switch (a1.ss_family) {
        case AF_INET: {
            const struct sockaddr_in* sa1 =
                reinterpret_cast<const struct sockaddr_in*>(&a1);
            const struct sockaddr_in* sa2 =
                reinterpret_cast<const struct sockaddr_in*>(&a2);

            if (sa1->sin_addr.s_addr != sa2->sin_addr.s_addr)
                return false;

            return (matchAddressOnly || (sa1->sin_port == sa2->sin_port));
        } break;

        case AF_INET6: {
            const struct sockaddr_in6* sa1 =
                reinterpret_cast<const struct sockaddr_in6*>(&a1);
            const struct sockaddr_in6* sa2 =
                reinterpret_cast<const struct sockaddr_in6*>(&a2);

            if (memcmp(&sa1->sin6_addr, &sa2->sin6_addr, sizeof(sa2->sin6_addr)))
                return false;

            return (matchAddressOnly || (sa1->sin6_port == sa2->sin6_port));
        } break;

        // Huh?  We don't deal in non-IPv[46] addresses.  Not sure how we got
        // here, but we don't know how to comapre these addresses and simply
        // default to a no-match decision.
        default: return false;
    }
}

bool CommonTimeServer::shouldPanicNotGettingGoodData() {
    if (mClient_FirstSyncTX) {
        int64_t now = mLocalClock.getLocalTime();
        int64_t delta = now - (mClient_LastGoodSyncRX
                             ? mClient_LastGoodSyncRX
                             : mClient_FirstSyncTX);
        int64_t deltaUsec = mCommonClock.localDurationToCommonDuration(delta);

        if (deltaUsec >= kNoGoodDataPanicThresholdUsec)
            return true;
    }

    return false;
}

void CommonTimeServer::PacketRTTLog::logTX(int64_t txTime) {
    txTimes[wrPtr] = txTime;
    rxTimes[wrPtr] = 0;
    wrPtr = (wrPtr + 1) % RTT_LOG_SIZE;
    if (!wrPtr)
        logFull = true;
}

void CommonTimeServer::PacketRTTLog::logRX(int64_t txTime, int64_t rxTime) {
    if (!logFull && !wrPtr)
        return;

    uint32_t i = logFull ? wrPtr : 0;
    do {
        if (txTimes[i] == txTime) {
            rxTimes[i] = rxTime;
            break;
        }
        i = (i + 1) % RTT_LOG_SIZE;
    } while (i != wrPtr);
}

}  // namespace android
