John Grossman 9684be35bf AAH: Fix a bug in the master election state machine.
Client's of Masters who disappear before the client has processed even
one timesync response should not enter the Ronin state.  Since they
have never synced to the old Master's timeline, by becoming Ronin they
run the risk of defeating other new Ronin's in the master election,
even though they don't have any idea what time it is on the old
Master's timeline and therefore are unsuitable to serve as the new
Master.  Instead, they should transition to the Initial state, where
other new Ronins (who do know what time it is) have the chance to step
in and serve as master of the old timeline.  If there are no other
Ronin who can do the job, then the old timeline is dead and by
transitioning to Initial, a new one will be generated.

Change-Id: Iaa95313bfe68a971be2764e252ebf4b34313013d
Signed-off-by: John Grossman <johngro@google.com>
2011-10-28 10:14:51 -04:00

1115 lines
36 KiB
C++

/*
* Copyright (C) 2011 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.
*/
#include <arpa/inet.h>
#include <assert.h>
#include <fcntl.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/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#define LOG_TAG "aah_timesrv"
#include <aah_timesrv/local_clock.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <utils/Log.h>
#include <utils/Timers.h>
#include "aah_common_clock_service.h"
#include "clock_recovery.h"
#include "common_clock.h"
namespace android {
/***** time sync protocol packets *****/
enum TimeServicePacketType {
TIME_PACKET_WHO_IS_MASTER_REQUEST = 1,
TIME_PACKET_WHO_IS_MASTER_RESPONSE,
TIME_PACKET_SYNC_REQUEST,
TIME_PACKET_SYNC_RESPONSE,
TIME_PACKET_MASTER_ANNOUNCEMENT,
};
struct TimeServicePacketHeader {
TimeServicePacketHeader(TimeServicePacketType type)
: magic(htonl(kMagic)),
packetType(htonl(type)),
kernelTxLocalTime(0),
kernelTxCommonTime(0),
kernelRxLocalTime(0) { }
TimeServicePacketType type() const {
return static_cast<TimeServicePacketType>(ntohl(packetType));
}
bool checkMagic() const {
return (ntohl(magic) == kMagic);
}
static const uint32_t kMagic;
// magic number identifying the protocol
uint32_t magic;
// TimeServicePacketType value
uint32_t packetType;
// placeholders for transmit/receive timestamps that can be filled in
// by a kernel netfilter driver
//
// local time (in the transmitter's domain) when this packet was sent
int64_t kernelTxLocalTime;
// common time when this packet was sent
int64_t kernelTxCommonTime;
// local time (in the receiver's domain) when this packet was received
int64_t kernelRxLocalTime;
} __attribute__((packed));
const uint32_t TimeServicePacketHeader::kMagic = 0x54756e67;
// packet querying for a suitable master
struct WhoIsMasterRequestPacket {
WhoIsMasterRequestPacket() : header(TIME_PACKET_WHO_IS_MASTER_REQUEST) {}
TimeServicePacketHeader header;
// device ID of the sender
uint64_t senderDeviceID;
// If this is kInvalidTimelineID, then any master can response to this
// request. If this is not kInvalidTimelineID, the only a master publishing
// the given timeline ID will respond.
uint32_t timelineID;
} __attribute__((packed));
// response to a WhoIsMaster request
struct WhoIsMasterResponsePacket {
WhoIsMasterResponsePacket() : header(TIME_PACKET_WHO_IS_MASTER_RESPONSE) {}
TimeServicePacketHeader header;
// the master's device ID
uint64_t deviceID;
// the timeline ID being published by this master
uint32_t timelineID;
} __attribute__((packed));
// packet sent by a client requesting correspondence between local
// and common time
struct SyncRequestPacket {
SyncRequestPacket() : header(TIME_PACKET_SYNC_REQUEST) {}
TimeServicePacketHeader header;
// timeline that the client is following
uint32_t timelineID;
// local time when this request was transmitted
int64_t clientTxLocalTime;
} __attribute__((packed));
// response to a sync request sent by the master
struct SyncResponsePacket {
SyncResponsePacket() : header(TIME_PACKET_SYNC_RESPONSE) {}
TimeServicePacketHeader header;
// flag that is set if the recipient of the sync request is not acting
// as a master for the requested timeline
uint32_t nak;
// local time when this request was transmitted by the client
int64_t clientTxLocalTime;
// common time when the master received the request
int64_t masterRxCommonTime;
// common time when the master transmitted the response
int64_t masterTxCommonTime;
} __attribute__((packed));
// announcement of the master's presence
struct MasterAnnouncementPacket {
MasterAnnouncementPacket() : header(TIME_PACKET_MASTER_ANNOUNCEMENT) {}
TimeServicePacketHeader header;
// the master's device ID
uint64_t deviceID;
// the timeline ID being published by this master
uint32_t timelineID;
} __attribute__((packed));
/***** time service implementation *****/
class AAHTimeService : public Thread {
public:
AAHTimeService();
~AAHTimeService();
private:
bool threadLoop();
bool runStateMachine();
bool setup();
void assignTimelineID();
bool assignDeviceID();
static bool arbitrateMaster(uint64_t deviceID1, uint64_t deviceID2);
bool handlePacket();
bool handleWhoIsMasterRequest (const WhoIsMasterRequestPacket* request,
const sockaddr_in& srcAddr);
bool handleWhoIsMasterResponse(const WhoIsMasterResponsePacket* response,
const sockaddr_in& srcAddr);
bool handleSyncRequest (const SyncRequestPacket* request,
const sockaddr_in& srcAddr);
bool handleSyncResponse (const SyncResponsePacket* response,
const sockaddr_in& srcAddr);
bool handleMasterAnnouncement (const MasterAnnouncementPacket* packet,
const sockaddr_in& srcAddr);
bool handleTimeout();
bool handleTimeoutInitial();
bool handleTimeoutClient();
bool handleTimeoutMaster();
bool handleTimeoutRonin();
bool handleTimeoutWaitForElection();
bool sendWhoIsMasterRequest();
bool sendSyncRequest();
bool sendMasterAnnouncement();
bool becomeClient(const sockaddr_in& masterAddr,
uint64_t masterDeviceID,
uint32_t timelineID);
bool becomeMaster();
bool becomeRonin();
bool becomeWaitForElection();
bool becomeInitial();
void notifyClockSync();
void notifyClockSyncLoss();
enum State {
// the device just came up and is trying to discover the master
STATE_INITIAL,
// the device is a client of a master
STATE_CLIENT,
// the device is acting as master
STATE_MASTER,
// the device has lost contact with its master and needs to participate
// in the election of a new master
STATE_RONIN,
// the device is waiting for announcement of the newly elected master
STATE_WAIT_FOR_ELECTION,
};
State mState;
static const char* stateToString(State s);
void setState(State s);
// interval in milliseconds of the state machine's timeout
int mTimeoutMs;
// common clock, local clock abstraction, and clock recovery loop
CommonClock mCommonClock;
LocalClock mLocalClock;
ClockRecoveryLoop mClockRecovery;
// implementation of ICommonClock
sp<AAHCommonClock> mICommonClock;
// UDP socket for the time sync protocol
int mSocket;
// unique ID of this device
uint64_t mDeviceID;
// timestamp captured when a packet is received
int64_t mLastPacketRxLocalTime;
// multicast address used for master queries and announcements
struct sockaddr_in mMulticastAddr;
// ID of the timeline that this device is following
uint32_t mTimelineID;
// flag for whether the clock has been synced to a timeline
bool mClockSynced;
/*** status while in the Initial state ***/
int mInitial_WhoIsMasterRequestTimeouts;
static const int kInitial_NumWhoIsMasterRetries;
static const int kInitial_WhoIsMasterTimeoutMs;
/*** status while in the Client state ***/
struct sockaddr_in mClient_MasterAddr;
uint64_t mClient_MasterDeviceID;
bool mClient_SeenFirstSyncResponse;
bool mClient_SyncRequestPending;
int mClient_SyncRequestTimeouts;
static const int kClient_SyncRequestIntervalMs;
static const int kClient_SyncRequestTimeoutMs;
static const int kClient_NumSyncRequestRetries;
/*** status while in the Master state ***/
static const int kMaster_AnnouncementIntervalMs;
/*** status while in the Ronin state ***/
int mRonin_WhoIsMasterRequestTimeouts;
static const int kRonin_NumWhoIsMasterRetries;
static const int kRonin_WhoIsMasterTimeoutMs;
/*** status while in the WaitForElection state ***/
static const int kWaitForElection_TimeoutMs;
static const char* kServiceAddr;
static const uint16_t kServicePort;
static const int kInfiniteTimeout;
};
// multicast IP address used by this protocol
const char* AAHTimeService::kServiceAddr = "224.128.87.87";
// UDP port used by this protocol
const uint16_t AAHTimeService::kServicePort = 8787;
// mTimeoutMs value representing an infinite timeout
const int AAHTimeService::kInfiniteTimeout = -1;
/*** Initial state constants ***/
// number of WhoIsMaster attempts sent before giving up
const int AAHTimeService::kInitial_NumWhoIsMasterRetries = 6;
// timeout used when waiting for a response to a WhoIsMaster request
const int AAHTimeService::kInitial_WhoIsMasterTimeoutMs = 500;
/*** Client state constants ***/
// interval between sync requests sent to the master
const int AAHTimeService::kClient_SyncRequestIntervalMs = 1000;
// timeout used when waiting for a response to a sync request
const int AAHTimeService::kClient_SyncRequestTimeoutMs = 400;
// number of sync requests that can fail before a client assumes its master
// is dead
const int AAHTimeService::kClient_NumSyncRequestRetries = 5;
/*** Master state constants ***/
// timeout between announcements by the master
const int AAHTimeService::kMaster_AnnouncementIntervalMs = 10000;
/*** Ronin state constants ***/
// number of WhoIsMaster attempts sent before declaring ourselves master
const int AAHTimeService::kRonin_NumWhoIsMasterRetries = 4;
// timeout used when waiting for a response to a WhoIsMaster request
const int AAHTimeService::kRonin_WhoIsMasterTimeoutMs = 500;
/*** WaitForElection state constants ***/
// how long do we wait for an announcement from a master before
// trying another election?
const int AAHTimeService::kWaitForElection_TimeoutMs = 5000;
AAHTimeService::AAHTimeService()
: Thread(false)
, mState(STATE_INITIAL)
, mTimeoutMs(kInfiniteTimeout)
, mClockRecovery(&mLocalClock, &mCommonClock)
, mSocket(-1)
, mDeviceID(0)
, mLastPacketRxLocalTime(0)
, mTimelineID(ICommonClock::kInvalidTimelineID)
, mClockSynced(false)
, mInitial_WhoIsMasterRequestTimeouts(0)
, mClient_MasterDeviceID(0)
, mClient_SeenFirstSyncResponse(false)
, mClient_SyncRequestPending(false)
, mClient_SyncRequestTimeouts(0)
, mRonin_WhoIsMasterRequestTimeouts(0) {
memset(&mMulticastAddr, 0, sizeof(mMulticastAddr));
memset(&mClient_MasterAddr, 0, sizeof(mClient_MasterAddr));
}
AAHTimeService::~AAHTimeService() {
if (mSocket != -1) {
close(mSocket);
mSocket = -1;
}
}
bool AAHTimeService::threadLoop() {
runStateMachine();
IPCThreadState::self()->stopProcess();
return false;
}
bool AAHTimeService::runStateMachine() {
if (!mLocalClock.initCheck())
return false;
if (!mCommonClock.init(mLocalClock.getLocalFreq()))
return false;
if (!setup())
return false;
// Enter the initial state; this will also send the first request to
// discover the master
becomeInitial();
// run the state machine
while (true) {
struct pollfd pfd = {mSocket, POLLIN, 0};
nsecs_t startNs = systemTime();
int rc = poll(&pfd, 1, mTimeoutMs);
int elapsedMs = ns2ms(systemTime() - startNs);
mLastPacketRxLocalTime = mLocalClock.getLocalTime();
if (rc == -1) {
LOGE("%s:%d poll failed", __PRETTY_FUNCTION__, __LINE__);
return false;
}
if (rc == 0) {
mTimeoutMs = kInfiniteTimeout;
if (!handleTimeout()) {
LOGE("handleTimeout failed");
}
} else {
if (mTimeoutMs != kInfiniteTimeout) {
mTimeoutMs = (mTimeoutMs > elapsedMs)
? mTimeoutMs - elapsedMs
: 0;
}
if (pfd.revents & POLLIN) {
if (!handlePacket()) {
LOGE("handlePacket failed");
}
}
}
}
return true;
}
bool AAHTimeService::setup() {
int rc;
// seed the random number generator (used to generated timeline IDs)
srand(static_cast<unsigned int>(systemTime()));
// open a UDP socket for the timeline serivce
mSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (mSocket == -1) {
LOGE("%s:%d socket failed", __PRETTY_FUNCTION__, __LINE__);
return false;
}
// initialize the multicast address
memset(&mMulticastAddr, 0, sizeof(mMulticastAddr));
mMulticastAddr.sin_family = AF_INET;
inet_aton(kServiceAddr, &mMulticastAddr.sin_addr);
mMulticastAddr.sin_port = htons(kServicePort);
// bind the socket to the time service port on all interfaces
struct sockaddr_in bindAddr;
memset(&bindAddr, 0, sizeof(bindAddr));
bindAddr.sin_family = AF_INET;
bindAddr.sin_addr.s_addr = htonl(INADDR_ANY);
bindAddr.sin_port = htons(kServicePort);
rc = bind(mSocket, reinterpret_cast<const sockaddr *>(&bindAddr),
sizeof(bindAddr));
if (rc) {
LOGE("%s:%d bind failed", __PRETTY_FUNCTION__, __LINE__);
return false;
}
// add the socket to the multicast group
struct ip_mreq mreq;
mreq.imr_multiaddr = mMulticastAddr.sin_addr;
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
rc = setsockopt(mSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&mreq, sizeof(mreq));
if (rc == -1) {
LOGE("%s:%d setsockopt failed (err = %d)",
__PRETTY_FUNCTION__, __LINE__, errno);
return false;
}
// disable loopback of multicast packets
const int zero = 0;
rc = setsockopt(mSocket, IPPROTO_IP, IP_MULTICAST_LOOP,
&zero, sizeof(zero));
if (rc == -1) {
LOGE("%s:%d setsockopt failed", __PRETTY_FUNCTION__, __LINE__);
return false;
}
// get the device's unique ID
if (!assignDeviceID())
return false;
// start the ICommonClock service
mICommonClock = AAHCommonClock::instantiate(&mCommonClock, &mLocalClock);
if (mICommonClock == NULL)
return false;
return true;
}
// generate a unique device ID that can be used for arbitration
bool AAHTimeService::assignDeviceID() {
// on the PandaBoard, derive the device ID from the MAC address of
// the eth0 interface
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_addr.sa_family = AF_INET;
strlcpy(ifr.ifr_name, "eth0", IFNAMSIZ);
int rc = ioctl(mSocket, SIOCGIFHWADDR, &ifr);
if (rc) {
LOGE("%s:%d ioctl failed", __PRETTY_FUNCTION__, __LINE__);
return false;
}
if (ifr.ifr_addr.sa_family != ARPHRD_ETHER) {
LOGE("%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 AAHTimeService::assignTimelineID() {
do {
mTimelineID = rand();
} 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 AAHTimeService::arbitrateMaster(uint64_t deviceID1,
uint64_t deviceID2) {
return (deviceID1 > deviceID2);
}
bool AAHTimeService::handlePacket() {
const int kMaxPacketSize = 100;
uint8_t buf[kMaxPacketSize];
struct sockaddr_in srcAddr;
socklen_t srcAddrLen = sizeof(srcAddr);
ssize_t recvBytes = recvfrom(
mSocket, buf, sizeof(buf), 0,
reinterpret_cast<const sockaddr *>(&srcAddr), &srcAddrLen);
if (recvBytes == -1) {
LOGE("%s:%d recvfrom failed", __PRETTY_FUNCTION__, __LINE__);
return false;
}
if (recvBytes < static_cast<ssize_t>(sizeof(TimeServicePacketHeader)))
return false;
TimeServicePacketHeader* header =
reinterpret_cast<TimeServicePacketHeader*>(buf);
if (!header->checkMagic())
return false;
bool result;
switch (header->type()) {
case TIME_PACKET_WHO_IS_MASTER_REQUEST: {
if (recvBytes <
static_cast<ssize_t>(sizeof(WhoIsMasterRequestPacket))) {
result = false;
} else {
result = handleWhoIsMasterRequest(
reinterpret_cast<WhoIsMasterRequestPacket*>(buf),
srcAddr);
}
} break;
case TIME_PACKET_WHO_IS_MASTER_RESPONSE: {
if (recvBytes <
static_cast<ssize_t>(sizeof(WhoIsMasterResponsePacket))) {
result = false;
} else {
result = handleWhoIsMasterResponse(
reinterpret_cast<WhoIsMasterResponsePacket*>(buf),
srcAddr);
}
} break;
case TIME_PACKET_SYNC_REQUEST: {
if (recvBytes < static_cast<ssize_t>(sizeof(SyncRequestPacket))) {
result = false;
} else {
result = handleSyncRequest(
reinterpret_cast<SyncRequestPacket*>(buf),
srcAddr);
}
} break;
case TIME_PACKET_SYNC_RESPONSE: {
if (recvBytes < static_cast<ssize_t>(sizeof(SyncResponsePacket))) {
result = false;
} else {
result = handleSyncResponse(
reinterpret_cast<SyncResponsePacket*>(buf),
srcAddr);
}
} break;
case TIME_PACKET_MASTER_ANNOUNCEMENT: {
if (recvBytes <
static_cast<ssize_t>(sizeof(MasterAnnouncementPacket))) {
result = false;
} else {
result = handleMasterAnnouncement(
reinterpret_cast<MasterAnnouncementPacket*>(buf),
srcAddr);
}
} break;
default: {
LOGD("%s:%d unknown packet type", __PRETTY_FUNCTION__, __LINE__);
result = false;
} break;
}
return result;
}
bool AAHTimeService::handleTimeout() {
switch (mState) {
case STATE_INITIAL:
return handleTimeoutInitial();
case STATE_CLIENT:
return handleTimeoutClient();
case STATE_MASTER:
return handleTimeoutMaster();
case STATE_RONIN:
return handleTimeoutRonin();
case STATE_WAIT_FOR_ELECTION:
return handleTimeoutWaitForElection();
}
return false;
}
bool AAHTimeService::handleTimeoutInitial() {
if (++mInitial_WhoIsMasterRequestTimeouts ==
kInitial_NumWhoIsMasterRetries) {
// none of our attempts to discover a master succeeded, so make
// this device the master
return becomeMaster();
} else {
// retry the WhoIsMaster request
return sendWhoIsMasterRequest();
}
}
bool AAHTimeService::handleTimeoutClient() {
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();
}
} else {
// initiate the next sync request
return sendSyncRequest();
}
}
bool AAHTimeService::handleTimeoutMaster() {
// send another announcement from the master
return sendMasterAnnouncement();
}
bool AAHTimeService::handleTimeoutRonin() {
if (++mRonin_WhoIsMasterRequestTimeouts == kRonin_NumWhoIsMasterRetries) {
// no other master is out there, so we won the election
return becomeMaster();
} else {
return sendWhoIsMasterRequest();
}
}
bool AAHTimeService::handleTimeoutWaitForElection() {
return becomeRonin();
}
bool AAHTimeService::handleWhoIsMasterRequest(
const WhoIsMasterRequestPacket* request,
const sockaddr_in& srcAddr) {
if (mState == STATE_MASTER) {
// is this request related to this master's timeline?
if (ntohl(request->timelineID) != ICommonClock::kInvalidTimelineID &&
ntohl(request->timelineID) != mTimelineID)
return true;
WhoIsMasterResponsePacket response;
response.deviceID = htonq(mDeviceID);
response.timelineID = htonl(mTimelineID);
ssize_t sendBytes = sendto(
mSocket, &response, sizeof(response), 0,
reinterpret_cast<const sockaddr *>(&srcAddr),
sizeof(srcAddr));
if (sendBytes == -1) {
LOGE("%s:%d sendto failed", __PRETTY_FUNCTION__, __LINE__);
return false;
}
} else if (mState == 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 (ntohl(request->timelineID) != mTimelineID)
return true;
if (arbitrateMaster(ntohq(request->senderDeviceID), mDeviceID))
return becomeWaitForElection();
return true;
} else if (mState == 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 (ntohl(request->timelineID) == ICommonClock::kInvalidTimelineID &&
arbitrateMaster(ntohq(request->senderDeviceID), mDeviceID)) {
mInitial_WhoIsMasterRequestTimeouts = 0;
}
}
return true;
}
bool AAHTimeService::handleWhoIsMasterResponse(
const WhoIsMasterResponsePacket* response,
const sockaddr_in& srcAddr) {
if (mState == STATE_INITIAL || mState == STATE_RONIN) {
return becomeClient(srcAddr,
ntohq(response->deviceID),
ntohl(response->timelineID));
} else if (mState == 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(ntohq(response->deviceID),
mClient_MasterDeviceID)) {
return becomeClient(srcAddr,
ntohq(response->deviceID),
ntohl(response->timelineID));
}
}
return true;
}
bool AAHTimeService::handleSyncRequest(const SyncRequestPacket* request,
const sockaddr_in& srcAddr) {
SyncResponsePacket response;
if (mState == STATE_MASTER && ntohl(request->timelineID) == mTimelineID) {
int64_t rxLocalTime = (request->header.kernelRxLocalTime) ?
ntohq(request->header.kernelRxLocalTime) : mLastPacketRxLocalTime;
int64_t rxCommonTime;
if (OK != mCommonClock.localToCommon(rxLocalTime, &rxCommonTime)) {
return false;
}
// TODO(johngro) : now that common time has moved out of the kernel, in
// order to turn netfilter based timestamping of transmit and receive
// times, we will need to make some changes to the sync request/resposne
// packet structure. Currently masters send back to clients RX and TX
// times expressed in common time (since the master's local time is not
// useful to the client). Now that the netfilter driver has no access
// to common time, then netfilter driver should capture the master's rx
// local time as the packet comes in, and put the master's tx local time
// into the packet as the response goes out. The user mode code (this
// function) needs to add the master's local->common transformation to
// the packet so that the client can make use of the data.
int64_t txLocalTime = mLocalClock.getLocalTime();;
int64_t txCommonTime;
if (OK != mCommonClock.localToCommon(txLocalTime, &txCommonTime)) {
return false;
}
response.nak = htonl(0);
response.clientTxLocalTime = (request->header.kernelTxLocalTime) ?
request->header.kernelTxLocalTime : request->clientTxLocalTime;
response.masterRxCommonTime = htonq(rxCommonTime);
response.masterTxCommonTime = htonq(txCommonTime);
} else {
response.nak = htonl(1);
response.clientTxLocalTime = htonl(0);
response.masterRxCommonTime = htonl(0);
response.masterTxCommonTime = htonl(0);
}
ssize_t sendBytes = sendto(
mSocket, &response, sizeof(response), 0,
reinterpret_cast<const sockaddr *>(&srcAddr),
sizeof(srcAddr));
if (sendBytes == -1) {
LOGE("%s:%d sendto failed", __PRETTY_FUNCTION__, __LINE__);
return false;
}
return true;
}
bool AAHTimeService::handleSyncResponse(
const SyncResponsePacket* response,
const sockaddr_in& srcAddr) {
if (mState != STATE_CLIENT)
return true;
if (ntohl(response->nak)) {
// if our master is no longer accepting requests, then we need to find
// a new master
return becomeRonin();
}
mClient_SyncRequestPending = 0;
mClient_SyncRequestTimeouts = 0;
bool result;
if (!mClient_SeenFirstSyncResponse) {
// the first request/response exchange between a client and a master
// may take unusually long due to ARP, so discard it.
mClient_SeenFirstSyncResponse = true;
result = true;
} else {
int64_t clientTxLocalTime = ntohq(response->clientTxLocalTime);
int64_t clientRxLocalTime = (response->header.kernelRxLocalTime)
? ntohq(response->header.kernelRxLocalTime)
: mLastPacketRxLocalTime;
int64_t masterTxCommonTime = (response->header.kernelTxCommonTime)
? ntohq(response->header.kernelTxCommonTime)
: ntohq(response->masterTxCommonTime);
int64_t masterRxCommonTime = ntohq(response->masterRxCommonTime);
int64_t rtt = (clientRxLocalTime - clientTxLocalTime);
int64_t avgLocal = (clientTxLocalTime + clientRxLocalTime) >> 1;
int64_t avgCommon = (masterTxCommonTime + masterRxCommonTime) >> 1;
result = mClockRecovery.pushDisciplineEvent(avgLocal, avgCommon, rtt);
if (result) {
// indicate to listeners that we've synced to the common timeline
notifyClockSync();
} else {
LOGE("Panic! Observed clock sync error is too high to tolerate,"
" resetting state machine and starting over.");
notifyClockSyncLoss();
return becomeInitial();
}
}
mTimeoutMs = kClient_SyncRequestIntervalMs;
return result;
}
bool AAHTimeService::handleMasterAnnouncement(
const MasterAnnouncementPacket* packet,
const sockaddr_in& srcAddr) {
uint64_t newDeviceID = ntohq(packet->deviceID);
uint32_t newTimelineID = ntohl(packet->timelineID);
if (mState == STATE_INITIAL ||
mState == STATE_RONIN ||
mState == STATE_WAIT_FOR_ELECTION) {
// if we aren't currently following a master, then start following
// this new master
return becomeClient(srcAddr, newDeviceID, newTimelineID);
} else if (mState == STATE_CLIENT) {
// if the new master wins arbitration against our current master,
// then become a client of the new master
if (arbitrateMaster(newDeviceID, mClient_MasterDeviceID))
return becomeClient(srcAddr, newDeviceID, newTimelineID);
} else if (mState == STATE_MASTER) {
// two masters are competing - if the new one wins arbitration, then
// cease acting as master
if (arbitrateMaster(newDeviceID, mDeviceID))
return becomeClient(srcAddr, newDeviceID, newTimelineID);
}
return true;
}
bool AAHTimeService::sendWhoIsMasterRequest() {
assert(mState == STATE_INITIAL || mState == STATE_RONIN);
WhoIsMasterRequestPacket request;
request.senderDeviceID = htonq(mDeviceID);
request.timelineID = htonl(mTimelineID);
ssize_t sendBytes = sendto(
mSocket, &request, sizeof(request), 0,
reinterpret_cast<const sockaddr *>(&mMulticastAddr),
sizeof(mMulticastAddr));
if (sendBytes == -1) {
LOGE("%s:%d sendto failed", __PRETTY_FUNCTION__, __LINE__);
}
if (mState == STATE_INITIAL) {
mTimeoutMs = kInitial_WhoIsMasterTimeoutMs;
} else {
mTimeoutMs = kRonin_WhoIsMasterTimeoutMs;
}
return (sendBytes != -1);
}
bool AAHTimeService::sendSyncRequest() {
assert(mState == STATE_CLIENT);
SyncRequestPacket request;
request.timelineID = htonl(mTimelineID);
request.clientTxLocalTime = htonq(mLocalClock.getLocalTime());
ssize_t sendBytes = sendto(
mSocket, &request, sizeof(request), 0,
reinterpret_cast<const sockaddr *>(&mClient_MasterAddr),
sizeof(mClient_MasterAddr));
if (sendBytes == -1) {
LOGE("%s:%d sendto failed", __PRETTY_FUNCTION__, __LINE__);
}
mTimeoutMs = kClient_SyncRequestTimeoutMs;
mClient_SyncRequestPending = true;
return (sendBytes != -1);
}
bool AAHTimeService::sendMasterAnnouncement() {
assert(mState == STATE_MASTER);
MasterAnnouncementPacket announce;
announce.deviceID = htonq(mDeviceID);
announce.timelineID = htonl(mTimelineID);
ssize_t sendBytes = sendto(
mSocket, &announce, sizeof(announce), 0,
reinterpret_cast<const sockaddr *>(&mMulticastAddr),
sizeof(mMulticastAddr));
if (sendBytes == -1) {
LOGE("%s:%d sendto failed", __PRETTY_FUNCTION__, __LINE__);
}
mTimeoutMs = kMaster_AnnouncementIntervalMs;
return (sendBytes != -1);
}
bool AAHTimeService::becomeClient(const sockaddr_in& masterAddr,
uint64_t masterDeviceID,
uint32_t timelineID) {
mClient_MasterAddr = masterAddr;
mClient_MasterDeviceID = masterDeviceID;
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);
}
mClient_SyncRequestPending = 0;
mClient_SyncRequestTimeouts = 0;
mClient_SeenFirstSyncResponse = false;
setState(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((rand() % 100) * 1000);
return sendSyncRequest();
}
bool AAHTimeService::becomeMaster() {
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();
}
mClockRecovery.reset(false, true);
setState(STATE_MASTER);
return sendMasterAnnouncement();
}
bool AAHTimeService::becomeRonin() {
// 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.
if (mCommonClock.isValid()) {
mRonin_WhoIsMasterRequestTimeouts = 0;
setState(STATE_RONIN);
return sendWhoIsMasterRequest();
} else {
return becomeInitial();
}
}
bool AAHTimeService::becomeWaitForElection() {
setState(STATE_WAIT_FOR_ELECTION);
mTimeoutMs = kWaitForElection_TimeoutMs;
return true;
}
bool AAHTimeService::becomeInitial() {
setState(STATE_INITIAL);
// reset clock recovery
mClockRecovery.reset(true, true);
// reset internal state bookkeeping.
mTimeoutMs = kInfiniteTimeout;
mLastPacketRxLocalTime = 0;
mTimelineID = ICommonClock::kInvalidTimelineID;
mClockSynced = false;
mInitial_WhoIsMasterRequestTimeouts = 0;
mClient_MasterDeviceID = 0;
mClient_SeenFirstSyncResponse = false;
mClient_SyncRequestPending = false;
mClient_SyncRequestTimeouts = 0;
mRonin_WhoIsMasterRequestTimeouts = 0;
// send the first request to discover the master
return sendWhoIsMasterRequest();
}
void AAHTimeService::notifyClockSync() {
if (!mClockSynced) {
mICommonClock->notifyOnClockSync(mTimelineID);
mClockSynced = true;
}
}
void AAHTimeService::notifyClockSyncLoss() {
if (mClockSynced) {
mICommonClock->notifyOnClockSyncLoss();
mClockSynced = false;
}
}
void AAHTimeService::setState(State s) {
mState = s;
LOGI("State transition; state is now %s", stateToString(s));
}
const char* AAHTimeService::stateToString(State s) {
switch(s) {
case STATE_INITIAL:
return "INITIAL";
case STATE_CLIENT:
return "CLIENT";
case STATE_MASTER:
return "MASTER";
case STATE_RONIN:
return "RONIN";
case STATE_WAIT_FOR_ELECTION:
return "WAIT_FOR_ELECTION";
default:
return "unknown";
}
}
} // namespace android
int main(int argc, char *argv[]) {
using namespace android;
sp<AAHTimeService> service = new AAHTimeService();
if (service == NULL)
return 1;
ProcessState::self()->startThreadPool();
service->run("AAHTimeService", ANDROID_PRIORITY_NORMAL);
IPCThreadState::self()->joinThreadPool();
return 0;
}