summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--services/common_time/Android.mk5
-rw-r--r--services/common_time/clock_recovery.cpp9
-rw-r--r--services/common_time/clock_recovery.h2
-rw-r--r--services/common_time/common_clock.cpp10
-rw-r--r--services/common_time/common_clock.h4
-rw-r--r--services/common_time/common_clock_service.cpp143
-rw-r--r--services/common_time/common_clock_service.h40
-rw-r--r--services/common_time/common_time_config_service.cpp52
-rw-r--r--services/common_time/common_time_config_service.h10
-rw-r--r--services/common_time/common_time_server.cpp1428
-rw-r--r--services/common_time/common_time_server.h330
-rw-r--r--services/common_time/common_time_server_api.cpp435
-rw-r--r--services/common_time/common_time_server_packets.cpp293
-rw-r--r--services/common_time/common_time_server_packets.h189
-rw-r--r--services/common_time/main.cpp43
15 files changed, 2261 insertions, 732 deletions
diff --git a/services/common_time/Android.mk b/services/common_time/Android.mk
index e8eb830..aabe572 100644
--- a/services/common_time/Android.mk
+++ b/services/common_time/Android.mk
@@ -10,8 +10,11 @@ LOCAL_SRC_FILES := \
common_clock_service.cpp \
common_time_config_service.cpp \
common_time_server.cpp \
+ common_time_server_api.cpp \
+ common_time_server_packets.cpp \
clock_recovery.cpp \
- common_clock.cpp
+ common_clock.cpp \
+ main.cpp
ifeq ($(TIME_SERVICE_DEBUG), true)
LOCAL_SRC_FILES += diag_thread.cpp
diff --git a/services/common_time/clock_recovery.cpp b/services/common_time/clock_recovery.cpp
index ab3b446..031c1c9 100644
--- a/services/common_time/clock_recovery.cpp
+++ b/services/common_time/clock_recovery.cpp
@@ -265,6 +265,15 @@ bool ClockRecoveryLoop::pushDisciplineEvent(int64_t local_time,
return true;
}
+int32_t ClockRecoveryLoop::getLastErrorEstimate() {
+ Mutex::Autolock lock(&lock_);
+
+ if (last_delta_valid_)
+ return last_delta_;
+ else
+ return ICommonClock::kErrorEstimateUnknown;
+}
+
void ClockRecoveryLoop::computePIDParams() {
// TODO(johngro) : add the ability to fetch parameters from the driver/board
// level in case they have a HW clock discipline solution with parameters
diff --git a/services/common_time/clock_recovery.h b/services/common_time/clock_recovery.h
index 70097e0..5c35c38 100644
--- a/services/common_time/clock_recovery.h
+++ b/services/common_time/clock_recovery.h
@@ -18,6 +18,7 @@
#define __CLOCK_RECOVERY_H__
#include <stdint.h>
+#include <common_time/ICommonClock.h>
#include <utils/LinearTransform.h>
#include <utils/threads.h>
@@ -39,6 +40,7 @@ class ClockRecoveryLoop {
bool pushDisciplineEvent(int64_t local_time,
int64_t nominal_common_time,
int64_t data_point_rtt);
+ int32_t getLastErrorEstimate();
private:
typedef struct {
diff --git a/services/common_time/common_clock.cpp b/services/common_time/common_clock.cpp
index 3120e3b..c8edf35 100644
--- a/services/common_time/common_clock.cpp
+++ b/services/common_time/common_clock.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -36,6 +36,7 @@ CommonClock::CommonClock() {
cur_trans_.b_zero = 0;
cur_trans_.a_to_b_numer = local_to_common_freq_numer_ = 1;
cur_trans_.a_to_b_denom = local_to_common_freq_denom_ = 1;
+ duration_trans_ = cur_trans_;
}
bool CommonClock::init(uint64_t local_freq) {
@@ -58,6 +59,7 @@ bool CommonClock::init(uint64_t local_freq) {
static_cast<uint32_t>(numer);
cur_trans_.a_to_b_denom = local_to_common_freq_denom_ =
static_cast<uint32_t>(denom);
+ duration_trans_ = cur_trans_;
return true;
}
@@ -86,6 +88,12 @@ status_t CommonClock::commonToLocal(int64_t common, int64_t *local_out) const {
return OK;
}
+int64_t CommonClock::localDurationToCommonDuration(int64_t localDur) const {
+ int64_t ret;
+ duration_trans_.doForwardTransform(localDur, &ret);
+ return ret;
+}
+
void CommonClock::setBasis(int64_t local, int64_t common) {
Mutex::Autolock lock(&lock_);
diff --git a/services/common_time/common_clock.h b/services/common_time/common_clock.h
index 8c24507..b786fdc 100644
--- a/services/common_time/common_clock.h
+++ b/services/common_time/common_clock.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -33,6 +33,7 @@ class CommonClock {
status_t localToCommon(int64_t local, int64_t *common_out) const;
status_t commonToLocal(int64_t common, int64_t *local_out) const;
+ int64_t localDurationToCommonDuration(int64_t localDur) const;
uint64_t getCommonFreq() const { return kCommonFreq; }
bool isValid() const { return cur_trans_valid_; }
status_t setSlew(int64_t change_time, int32_t ppm);
@@ -45,6 +46,7 @@ class CommonClock {
uint32_t local_to_common_freq_numer_;
uint32_t local_to_common_freq_denom_;
+ LinearTransform duration_trans_;
LinearTransform cur_trans_;
bool cur_trans_valid_;
diff --git a/services/common_time/common_clock_service.cpp b/services/common_time/common_clock_service.cpp
index 5a66e16..9ca6f35 100644
--- a/services/common_time/common_clock_service.cpp
+++ b/services/common_time/common_clock_service.cpp
@@ -15,165 +15,142 @@
*/
#include <common_time/local_clock.h>
-#include <binder/IServiceManager.h>
-#include <binder/IPCThreadState.h>
#include <utils/String8.h>
#include "common_clock_service.h"
#include "common_clock.h"
+#include "common_time_server.h"
namespace android {
-bool CommonClockService::init(CommonClock* common_clock,
- LocalClock* local_clock) {
- mCommonClock = common_clock;
- mLocalClock = local_clock;
- mTimelineID = kInvalidTimelineID;
-
- return ((NULL != mCommonClock) && (NULL != mLocalClock));
-}
-
-status_t CommonClockService::dump(int fd, const Vector<String16>& args) {
- const size_t SIZE = 256;
- char buffer[SIZE];
-
- if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
- snprintf(buffer, SIZE, "Permission Denial: "
- "can't dump CommonClockService from pid=%d, uid=%d\n",
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid());
- } else {
- int64_t localTime = mLocalClock->getLocalTime();
- int64_t commonTime;
- bool synced = (OK == mCommonClock->localToCommon(localTime,
- &commonTime));
- if (synced) {
- snprintf(buffer, SIZE,
- "Common time synced\nLocal time: %lld\nCommon time: %lld\n"
- "Timeline ID: %u\n",
- localTime, commonTime, mTimelineID);
- } else {
- snprintf(buffer, SIZE,
- "Common time not synced\nLocal time: %lld\n",
- localTime);
- }
- }
-
- write(fd, buffer, strlen(buffer));
- return NO_ERROR;
-}
-
sp<CommonClockService> CommonClockService::instantiate(
- CommonClock* common_clock,
- LocalClock* local_clock) {
- sp<CommonClockService> tcc = new CommonClockService();
- if (tcc == NULL || !tcc->init(common_clock, local_clock))
+ CommonTimeServer& timeServer) {
+ sp<CommonClockService> tcc = new CommonClockService(timeServer);
+ if (tcc == NULL)
return NULL;
defaultServiceManager()->addService(ICommonClock::kServiceName, tcc);
return tcc;
}
+status_t CommonClockService::dump(int fd, const Vector<String16>& args) {
+ Mutex::Autolock lock(mRegistrationLock);
+ return mTimeServer.dumpClockInterface(fd, args, mListeners.size());
+}
+
status_t CommonClockService::isCommonTimeValid(bool* valid,
uint32_t* timelineID) {
- Mutex::Autolock lock(mLock);
-
- *valid = mCommonClock->isValid();
- *timelineID = mTimelineID;
- return OK;
+ return mTimeServer.isCommonTimeValid(valid, timelineID);
}
status_t CommonClockService::commonTimeToLocalTime(int64_t commonTime,
int64_t* localTime) {
- return mCommonClock->commonToLocal(commonTime, localTime);
+ return mTimeServer.getCommonClock().commonToLocal(commonTime, localTime);
}
status_t CommonClockService::localTimeToCommonTime(int64_t localTime,
int64_t* commonTime) {
- return mCommonClock->localToCommon(localTime, commonTime);
+ return mTimeServer.getCommonClock().localToCommon(localTime, commonTime);
}
status_t CommonClockService::getCommonTime(int64_t* commonTime) {
- return localTimeToCommonTime(mLocalClock->getLocalTime(), commonTime);
+ return localTimeToCommonTime(mTimeServer.getLocalClock().getLocalTime(), commonTime);
}
status_t CommonClockService::getCommonFreq(uint64_t* freq) {
- *freq = mCommonClock->getCommonFreq();
+ *freq = mTimeServer.getCommonClock().getCommonFreq();
return OK;
}
status_t CommonClockService::getLocalTime(int64_t* localTime) {
- *localTime = mLocalClock->getLocalTime();
+ *localTime = mTimeServer.getLocalClock().getLocalTime();
return OK;
}
status_t CommonClockService::getLocalFreq(uint64_t* freq) {
- *freq = mLocalClock->getLocalFreq();
+ *freq = mTimeServer.getLocalClock().getLocalFreq();
return OK;
}
status_t CommonClockService::getEstimatedError(int32_t* estimate) {
- return UNKNOWN_ERROR;
+ *estimate = mTimeServer.getEstimatedError();
+ return OK;
}
status_t CommonClockService::getTimelineID(uint64_t* id) {
- return UNKNOWN_ERROR;
+ *id = mTimeServer.getTimelineID();
+ return OK;
}
status_t CommonClockService::getState(State* state) {
- return UNKNOWN_ERROR;
+ *state = mTimeServer.getState();
+ return OK;
}
status_t CommonClockService::getMasterAddr(struct sockaddr_storage* addr) {
- return UNKNOWN_ERROR;
+ return mTimeServer.getMasterAddr(addr);
}
status_t CommonClockService::registerListener(
const sp<ICommonClockListener>& listener) {
- Mutex::Autolock lock(mLock);
-
- // check whether this is a duplicate
- for (size_t i = 0; i < mListeners.size(); i++) {
- if (mListeners[i]->asBinder() == listener->asBinder())
- return ALREADY_EXISTS;
+ Mutex::Autolock lock(mRegistrationLock);
+
+ { // scoping for autolock pattern
+ Mutex::Autolock lock(mCallbackLock);
+ // check whether this is a duplicate
+ for (size_t i = 0; i < mListeners.size(); i++) {
+ if (mListeners[i]->asBinder() == listener->asBinder())
+ return ALREADY_EXISTS;
+ }
}
mListeners.add(listener);
+ mTimeServer.reevaluateAutoDisableState(0 != mListeners.size());
return listener->asBinder()->linkToDeath(this);
}
status_t CommonClockService::unregisterListener(
const sp<ICommonClockListener>& listener) {
- Mutex::Autolock lock(mLock);
-
- for (size_t i = 0; i < mListeners.size(); i++) {
- if (mListeners[i]->asBinder() == listener->asBinder()) {
- mListeners[i]->asBinder()->unlinkToDeath(this);
- mListeners.removeAt(i);
- return OK;
+ Mutex::Autolock lock(mRegistrationLock);
+ status_t ret_val = NAME_NOT_FOUND;
+
+ { // scoping for autolock pattern
+ Mutex::Autolock lock(mCallbackLock);
+ for (size_t i = 0; i < mListeners.size(); i++) {
+ if (mListeners[i]->asBinder() == listener->asBinder()) {
+ mListeners[i]->asBinder()->unlinkToDeath(this);
+ mListeners.removeAt(i);
+ ret_val = OK;
+ break;
+ }
}
}
- return NAME_NOT_FOUND;
+ mTimeServer.reevaluateAutoDisableState(0 != mListeners.size());
+ return ret_val;
}
void CommonClockService::binderDied(const wp<IBinder>& who) {
- Mutex::Autolock lock(mLock);
-
- for (size_t i = 0; i < mListeners.size(); i++) {
- if (mListeners[i]->asBinder() == who) {
- mListeners.removeAt(i);
- return;
+ Mutex::Autolock lock(mRegistrationLock);
+
+ { // scoping for autolock pattern
+ Mutex::Autolock lock(mCallbackLock);
+ for (size_t i = 0; i < mListeners.size(); i++) {
+ if (mListeners[i]->asBinder() == who) {
+ mListeners.removeAt(i);
+ break;
+ }
}
}
+
+ mTimeServer.reevaluateAutoDisableState(0 != mListeners.size());
}
void CommonClockService::notifyOnTimelineChanged(uint64_t timelineID) {
- Mutex::Autolock lock(mLock);
+ Mutex::Autolock lock(mCallbackLock);
- mTimelineID = timelineID;
for (size_t i = 0; i < mListeners.size(); i++) {
- mListeners[i]->onTimelineChanged(mTimelineID);
+ mListeners[i]->onTimelineChanged(timelineID);
}
}
diff --git a/services/common_time/common_clock_service.h b/services/common_time/common_clock_service.h
index f648427..a65e398 100644
--- a/services/common_time/common_clock_service.h
+++ b/services/common_time/common_clock_service.h
@@ -21,14 +21,12 @@
namespace android {
-class CommonClock;
-class LocalClock;
+class CommonTimeServer;
class CommonClockService : public BnCommonClock,
public android::IBinder::DeathRecipient {
public:
- static sp<CommonClockService> instantiate(CommonClock* common_clock,
- LocalClock* local_clock);
+ static sp<CommonClockService> instantiate(CommonTimeServer& timeServer);
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -54,18 +52,36 @@ class CommonClockService : public BnCommonClock,
void notifyOnTimelineChanged(uint64_t timelineID);
private:
- CommonClockService() {}
- bool init(CommonClock* common_clock, LocalClock* local_clock);
+ CommonClockService(CommonTimeServer& timeServer)
+ : mTimeServer(timeServer) { };
virtual void binderDied(const wp<IBinder>& who);
- CommonClock* mCommonClock;
- LocalClock* mLocalClock;
+ CommonTimeServer& mTimeServer;
+
+ // locks used to synchronize access to the list of registered listeners.
+ // The callback lock is held whenever the list is used to perform callbacks
+ // or while the list is being modified. The registration lock used to
+ // serialize access across registerListener, unregisterListener, and
+ // binderDied.
+ //
+ // The reason for two locks is that registerListener, unregisterListener,
+ // and binderDied each call into the core service and obtain the core
+ // service thread lock when they call reevaluateAutoDisableState. The core
+ // service thread obtains the main thread lock whenever its thread is
+ // running, and sometimes needs to call notifyOnTimelineChanged which then
+ // obtains the callback lock. If callers of registration functions were
+ // holding the callback lock when they called into the core service, we
+ // would have a classic A/B, B/A ordering deadlock. To avoid this, the
+ // registration functions hold the registration lock for the duration of
+ // their call, but hold the callback lock only while they mutate the list.
+ // This way, the list's size cannot change (because of the registration
+ // lock) during the call into reevaluateAutoDisableState, but the core work
+ // thread can still safely call notifyOnTimelineChanged while holding the
+ // main thread lock.
+ Mutex mCallbackLock;
+ Mutex mRegistrationLock;
- // this lock serializes access to mTimelineID and mListeners
- Mutex mLock;
-
- uint32_t mTimelineID;
Vector<sp<ICommonClockListener> > mListeners;
};
diff --git a/services/common_time/common_time_config_service.cpp b/services/common_time/common_time_config_service.cpp
index 1001ccb..9585618 100644
--- a/services/common_time/common_time_config_service.cpp
+++ b/services/common_time/common_time_config_service.cpp
@@ -14,87 +14,99 @@
* limitations under the License.
*/
-#include <binder/IServiceManager.h>
+#include <utils/String8.h>
#include "common_time_config_service.h"
+#include "common_time_server.h"
namespace android {
-sp<CommonTimeConfigService> CommonTimeConfigService::instantiate() {
- sp<CommonTimeConfigService> ctcs = new CommonTimeConfigService();
+sp<CommonTimeConfigService> CommonTimeConfigService::instantiate(
+ CommonTimeServer& timeServer) {
+ sp<CommonTimeConfigService> ctcs = new CommonTimeConfigService(timeServer);
+ if (ctcs == NULL)
+ return NULL;
defaultServiceManager()->addService(ICommonTimeConfig::kServiceName, ctcs);
return ctcs;
}
+status_t CommonTimeConfigService::dump(int fd, const Vector<String16>& args) {
+ return mTimeServer.dumpConfigInterface(fd, args);
+}
+
status_t CommonTimeConfigService::getMasterElectionPriority(uint8_t *priority) {
- return UNKNOWN_ERROR;
+ return mTimeServer.getMasterElectionPriority(priority);
}
status_t CommonTimeConfigService::setMasterElectionPriority(uint8_t priority) {
- return UNKNOWN_ERROR;
+ return mTimeServer.setMasterElectionPriority(priority);
}
status_t CommonTimeConfigService::getMasterElectionEndpoint(
struct sockaddr_storage *addr) {
- return UNKNOWN_ERROR;
+ return mTimeServer.getMasterElectionEndpoint(addr);
}
status_t CommonTimeConfigService::setMasterElectionEndpoint(
const struct sockaddr_storage *addr) {
- return UNKNOWN_ERROR;
+ return mTimeServer.setMasterElectionEndpoint(addr);
}
status_t CommonTimeConfigService::getMasterElectionGroupId(uint64_t *id) {
- return UNKNOWN_ERROR;
+ return mTimeServer.getMasterElectionGroupId(id);
}
status_t CommonTimeConfigService::setMasterElectionGroupId(uint64_t id) {
- return UNKNOWN_ERROR;
+ return mTimeServer.setMasterElectionGroupId(id);
}
status_t CommonTimeConfigService::getInterfaceBinding(String16& ifaceName) {
- return UNKNOWN_ERROR;
+ String8 tmp;
+ status_t ret = mTimeServer.getInterfaceBinding(tmp);
+ ifaceName = String16(tmp);
+ return ret;
}
status_t CommonTimeConfigService::setInterfaceBinding(const String16& ifaceName) {
- return UNKNOWN_ERROR;
+ String8 tmp(ifaceName);
+ return mTimeServer.setInterfaceBinding(tmp);
}
status_t CommonTimeConfigService::getMasterAnnounceInterval(int *interval) {
- return UNKNOWN_ERROR;
+ return mTimeServer.getMasterAnnounceInterval(interval);
}
status_t CommonTimeConfigService::setMasterAnnounceInterval(int interval) {
- return UNKNOWN_ERROR;
+ return mTimeServer.setMasterAnnounceInterval(interval);
}
status_t CommonTimeConfigService::getClientSyncInterval(int *interval) {
- return UNKNOWN_ERROR;
+ return mTimeServer.getClientSyncInterval(interval);
}
status_t CommonTimeConfigService::setClientSyncInterval(int interval) {
- return UNKNOWN_ERROR;
+ return mTimeServer.setClientSyncInterval(interval);
}
status_t CommonTimeConfigService::getPanicThreshold(int *threshold) {
- return UNKNOWN_ERROR;
+ return mTimeServer.getPanicThreshold(threshold);
}
status_t CommonTimeConfigService::setPanicThreshold(int threshold) {
- return UNKNOWN_ERROR;
+ return mTimeServer.setPanicThreshold(threshold);
}
status_t CommonTimeConfigService::getAutoDisable(bool *autoDisable) {
- return UNKNOWN_ERROR;
+ return mTimeServer.getAutoDisable(autoDisable);
}
status_t CommonTimeConfigService::setAutoDisable(bool autoDisable) {
- return UNKNOWN_ERROR;
+ return mTimeServer.setAutoDisable(autoDisable);
}
status_t CommonTimeConfigService::forceNetworklessMasterMode() {
- return UNKNOWN_ERROR;
+ return mTimeServer.forceNetworklessMasterMode();
}
}; // namespace android
diff --git a/services/common_time/common_time_config_service.h b/services/common_time/common_time_config_service.h
index be582cc..8283c24 100644
--- a/services/common_time/common_time_config_service.h
+++ b/services/common_time/common_time_config_service.h
@@ -21,10 +21,13 @@
namespace android {
class String16;
+class CommonTimeServer;
class CommonTimeConfigService : public BnCommonTimeConfig {
public:
- static sp<CommonTimeConfigService> instantiate();
+ static sp<CommonTimeConfigService> instantiate(CommonTimeServer& timeServer);
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
virtual status_t getMasterElectionPriority(uint8_t *priority);
virtual status_t setMasterElectionPriority(uint8_t priority);
@@ -45,7 +48,10 @@ class CommonTimeConfigService : public BnCommonTimeConfig {
virtual status_t forceNetworklessMasterMode();
private:
- CommonTimeConfigService() {}
+ CommonTimeConfigService(CommonTimeServer& timeServer)
+ : mTimeServer(timeServer) { }
+ CommonTimeServer& mTimeServer;
+
};
}; // namespace android
diff --git a/services/common_time/common_time_server.cpp b/services/common_time/common_time_server.cpp
index 5b5f501..761265b 100644
--- a/services/common_time/common_time_server.cpp
+++ b/services/common_time/common_time_server.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -19,285 +19,53 @@
* 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 <limits>
#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>
-#define LOG_TAG "common_time"
-
#include <common_time/local_clock.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
-#include <utils/Log.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"
-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;
+using std::numeric_limits;
- // 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 CommonTimeServer : public Thread {
- public:
- CommonTimeServer();
- ~CommonTimeServer();
-
- 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();
-
- ICommonClock::State mState;
- static const char* stateToString(ICommonClock::State s);
- void setState(ICommonClock::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<CommonClockService> mICommonClock;
-
- // implementation of ICommonTimeConfig
- sp<CommonTimeConfigService> mICommonTimeConfig;
-
- // 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_SyncRequestPending;
- int mClient_SyncRequestTimeouts;
- uint32_t mClient_SyncsSentToCurMaster;
- uint32_t mClient_SyncRespsRvcedFromCurMaster;
- 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* CommonTimeServer::kServiceAddr = "224.128.87.87";
-
-// UDP port used by this protocol
-const uint16_t CommonTimeServer::kServicePort = 8787;
+namespace android {
-// mTimeoutMs value representing an infinite timeout
+const char* CommonTimeServer::kDefaultMasterElectionAddr = "239.195.128.88";
+const uint16_t CommonTimeServer::kDefaultMasterElectionPort = 8887;
+const uint64_t CommonTimeServer::kDefaultSyncGroupID = 0;
+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::kSetupRetryTimeout = 30000;
+const int64_t CommonTimeServer::kNoGoodDataPanicThreshold = 600000000ll;
+
+// timeout value representing an infinite timeout
const int CommonTimeServer::kInfiniteTimeout = -1;
/*** Initial state constants ***/
@@ -310,21 +78,12 @@ const int CommonTimeServer::kInitial_WhoIsMasterTimeoutMs = 500;
/*** Client state constants ***/
-// interval between sync requests sent to the master
-const int CommonTimeServer::kClient_SyncRequestIntervalMs = 1000;
-
-// timeout used when waiting for a response to a sync request
-const int CommonTimeServer::kClient_SyncRequestTimeoutMs = 400;
-
// number of sync requests that can fail before a client assumes its master
// is dead
const int CommonTimeServer::kClient_NumSyncRequestRetries = 5;
/*** Master state constants ***/
-// timeout between announcements by the master
-const int CommonTimeServer::kMaster_AnnouncementIntervalMs = 10000;
-
/*** Ronin state constants ***/
// number of WhoIsMaster attempts sent before declaring ourselves master
@@ -342,155 +101,405 @@ const int CommonTimeServer::kWaitForElection_TimeoutMs = 5000;
CommonTimeServer::CommonTimeServer()
: Thread(false)
, mState(ICommonClock::STATE_INITIAL)
- , mTimeoutMs(kInfiniteTimeout)
, mClockRecovery(&mLocalClock, &mCommonClock)
, mSocket(-1)
- , mDeviceID(0)
, mLastPacketRxLocalTime(0)
, mTimelineID(ICommonClock::kInvalidTimelineID)
, mClockSynced(false)
+ , mCommonClockHasClients(false)
, mInitial_WhoIsMasterRequestTimeouts(0)
, mClient_MasterDeviceID(0)
- , mClient_SyncRequestPending(false)
- , mClient_SyncRequestTimeouts(0)
- , mClient_SyncsSentToCurMaster(0)
- , mClient_SyncRespsRvcedFromCurMaster(0)
+ , mClient_MasterDevicePriority(0)
, mRonin_WhoIsMasterRequestTimeouts(0) {
- memset(&mMulticastAddr, 0, sizeof(mMulticastAddr));
- memset(&mClient_MasterAddr, 0, sizeof(mClient_MasterAddr));
+ // 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);
+
+ // seed the random number generator (used to generated timeline IDs)
+ srand48(static_cast<unsigned int>(systemTime()));
}
CommonTimeServer::~CommonTimeServer() {
- if (mSocket != -1) {
- close(mSocket);
- mSocket = -1;
+ 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() {
- runStateMachine();
+ // 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() {
+bool CommonTimeServer::runStateMachine_l() {
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();
+ // Enter the initial state.
+ becomeInitial("startup");
// 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();
+ while (!exitPending()) {
+ struct pollfd pfds[2];
+ int rc;
+ int eventCnt = 0;
+ int64_t wakeupTime;
+
+ // 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++;
+ }
- if (rc == -1) {
+ // 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, mCurTimeout.msecTillTimeout());
+ 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) {
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 (rc == 0)
+ 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;
+ }
- if (pfd.revents & POLLIN) {
- if (!handlePacket()) {
- LOGE("handlePacket failed");
+ // 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 send out a master
+ // announcement at low priority.
+ case CommonClockService::STATE_MASTER:
+ sendMasterAnnouncement();
+ 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(kSetupRetryTimeout);
}
+
+ // 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.
+ LOGI("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;
+ }
+
+ // Did we wakeup with no signalled events across all of our FDs? If so,
+ // we must have hit our timeout.
+ if (rc == 0) {
+ if (!handleTimeout())
+ LOGE("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())
+ LOGE("handlePacket failed");
}
}
+ cleanupSocket_l();
return true;
}
-bool CommonTimeServer::setup() {
+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;
- // seed the random number generator (used to generated timeline IDs)
- srand(static_cast<unsigned int>(systemTime()));
+ sockaddrToString(mMasterElectionEP, true, masterElectionEPStr,
+ sizeof(masterElectionEPStr));
+ LOGI("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) {
+ LOGW("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 == -1) {
- LOGE("%s:%d socket failed", __PRETTY_FUNCTION__, __LINE__);
- return false;
+ if (mSocket < 0) {
+ LOGE("Failed to create socket (errno = %d)", errno);
+ goto bailout;
}
- // 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 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) {
+ LOGE("Failed to bind socket at to interface %s (errno = %d)",
+ ifr.ifr_name, errno);
+ goto bailout;
+ }
- // bind the socket to the time service port on all interfaces
+ // 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;
- 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));
+ 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) {
- LOGE("%s:%d bind failed", __PRETTY_FUNCTION__, __LINE__);
- return false;
+ LOGE("Failed to bind socket to port %hu (errno = %d)",
+ ntohs(bindAddr.sin_port), errno);
+ goto bailout;
}
- // 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;
+ 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) {
+ LOGE("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) {
+ LOGE("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 neither broadcast, nor multicast,
+ // then we are misconfigured. The config API layer should prevent this
+ // from ever happening.
+ goto bailout;
}
- // disable loopback of multicast packets
- const int zero = 0;
- rc = setsockopt(mSocket, IPPROTO_IP, IP_MULTICAST_LOOP,
- &zero, sizeof(zero));
+ // 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) {
- LOGE("%s:%d setsockopt failed", __PRETTY_FUNCTION__, __LINE__);
- return false;
+ LOGE("Failed to set TTL to %d (errno = %d)", one, errno);
+ goto bailout;
}
// get the device's unique ID
if (!assignDeviceID())
- return false;
-
- // start the ICommonClock service
- mICommonClock = CommonClockService::instantiate(&mCommonClock, &mLocalClock);
- if (mICommonClock == NULL)
- return false;
+ goto bailout;
- // start the ICommonTimeConfig service
- mICommonTimeConfig = CommonTimeConfigService::instantiate();
- if (mICommonTimeConfig == NULL)
- return false;
+ ret_val = true;
- return true;
+bailout:
+ if (!ret_val)
+ cleanupSocket_l();
+ return ret_val;
}
// generate a unique device ID that can be used for arbitration
@@ -524,99 +533,67 @@ bool CommonTimeServer::assignDeviceID() {
// generate a new timeline ID
void CommonTimeServer::assignTimelineID() {
do {
- mTimelineID = rand();
+ 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,
- uint64_t deviceID2) {
- return (deviceID1 > deviceID2);
+bool CommonTimeServer::arbitrateMaster(
+ uint64_t deviceID1, uint8_t devicePrio1,
+ uint64_t deviceID2, uint8_t devicePrio2) {
+ return ((devicePrio1 > devicePrio2) ||
+ ((devicePrio1 == devicePrio2) && (deviceID1 > deviceID2)));
}
bool CommonTimeServer::handlePacket() {
- const int kMaxPacketSize = 100;
- uint8_t buf[kMaxPacketSize];
- struct sockaddr_in srcAddr;
+ 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 == -1) {
+ if (recvBytes < 0) {
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())
+ UniversalTimeServicePacket pkt;
+ recvBytes = pkt.deserializePacket(buf, recvBytes, mSyncGroupID);
+ if (recvBytes < 0)
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;
+ 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: {
- LOGD("%s:%d unknown packet type", __PRETTY_FUNCTION__, __LINE__);
+ LOGD("%s:%d unknown packet type(%d)",
+ __PRETTY_FUNCTION__, __LINE__, pkt.packetType);
result = false;
} break;
}
@@ -625,6 +602,10 @@ bool CommonTimeServer::handlePacket() {
}
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();
@@ -646,7 +627,7 @@ bool CommonTimeServer::handleTimeoutInitial() {
kInitial_NumWhoIsMasterRetries) {
// none of our attempts to discover a master succeeded, so make
// this device the master
- return becomeMaster();
+ return becomeMaster("initial timeout");
} else {
// retry the WhoIsMaster request
return sendWhoIsMasterRequest();
@@ -654,6 +635,9 @@ bool CommonTimeServer::handleTimeoutInitial() {
}
bool CommonTimeServer::handleTimeoutClient() {
+ if (shouldPanicNotGettingGoodData())
+ return becomeInitial("timeout panic, no good data");
+
if (mClient_SyncRequestPending) {
mClient_SyncRequestPending = false;
@@ -664,7 +648,7 @@ bool CommonTimeServer::handleTimeoutClient() {
// 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();
+ return becomeRonin("master not responding");
}
} else {
// initiate the next sync request
@@ -680,31 +664,37 @@ bool CommonTimeServer::handleTimeoutMaster() {
bool CommonTimeServer::handleTimeoutRonin() {
if (++mRonin_WhoIsMasterRequestTimeouts == kRonin_NumWhoIsMasterRetries) {
// no other master is out there, so we won the election
- return becomeMaster();
+ return becomeMaster("no better masters detected");
} else {
return sendWhoIsMasterRequest();
}
}
bool CommonTimeServer::handleTimeoutWaitForElection() {
- return becomeRonin();
+ return becomeRonin("timeout waiting for election conclusion");
}
bool CommonTimeServer::handleWhoIsMasterRequest(
const WhoIsMasterRequestPacket* request,
- const sockaddr_in& srcAddr) {
+ const sockaddr_storage& srcAddr) {
if (mState == ICommonClock::STATE_MASTER) {
// is this request related to this master's timeline?
- if (ntohl(request->timelineID) != ICommonClock::kInvalidTimelineID &&
- ntohl(request->timelineID) != mTimelineID)
+ if (request->timelineID != ICommonClock::kInvalidTimelineID &&
+ request->timelineID != mTimelineID)
return true;
- WhoIsMasterResponsePacket response;
- response.deviceID = htonq(mDeviceID);
- response.timelineID = htonl(mTimelineID);
+ WhoIsMasterResponsePacket 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)
+ return false;
ssize_t sendBytes = sendto(
- mSocket, &response, sizeof(response), 0,
+ mSocket, buf, bufSz, 0,
reinterpret_cast<const sockaddr *>(&srcAddr),
sizeof(srcAddr));
if (sendBytes == -1) {
@@ -716,11 +706,14 @@ bool CommonTimeServer::handleWhoIsMasterRequest(
// 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)
+ if (request->timelineID != mTimelineID)
return true;
- if (arbitrateMaster(ntohq(request->senderDeviceID), mDeviceID))
- return becomeWaitForElection();
+ if (arbitrateMaster(request->senderDeviceID,
+ request->senderDevicePriority,
+ mDeviceID,
+ effectivePriority()))
+ return becomeWaitForElection("would lose election");
return true;
} else if (mState == ICommonClock::STATE_INITIAL) {
@@ -731,8 +724,11 @@ bool CommonTimeServer::handleWhoIsMasterRequest(
// 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)) {
+ if (request->timelineID == ICommonClock::kInvalidTimelineID &&
+ arbitrateMaster(request->senderDeviceID,
+ request->senderDevicePriority,
+ mDeviceID,
+ effectivePriority())) {
mInitial_WhoIsMasterRequestTimeouts = 0;
}
}
@@ -742,20 +738,26 @@ bool CommonTimeServer::handleWhoIsMasterRequest(
bool CommonTimeServer::handleWhoIsMasterResponse(
const WhoIsMasterResponsePacket* response,
- const sockaddr_in& srcAddr) {
+ const sockaddr_storage& srcAddr) {
if (mState == ICommonClock::STATE_INITIAL || mState == ICommonClock::STATE_RONIN) {
return becomeClient(srcAddr,
- ntohq(response->deviceID),
- ntohl(response->timelineID));
+ 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(ntohq(response->deviceID),
- mClient_MasterDeviceID)) {
+ if (arbitrateMaster(response->deviceID,
+ response->devicePriority,
+ mClient_MasterDeviceID,
+ mClient_MasterDevicePriority)) {
return becomeClient(srcAddr,
- ntohq(response->deviceID),
- ntohl(response->timelineID));
+ response->deviceID,
+ response->devicePriority,
+ response->timelineID,
+ "heard whois response");
}
}
@@ -763,48 +765,47 @@ bool CommonTimeServer::handleWhoIsMasterResponse(
}
bool CommonTimeServer::handleSyncRequest(const SyncRequestPacket* request,
- const sockaddr_in& srcAddr) {
- SyncResponsePacket response;
- if (mState == ICommonClock::STATE_MASTER && ntohl(request->timelineID) == mTimelineID) {
- int64_t rxLocalTime = (request->header.kernelRxLocalTime) ?
- ntohq(request->header.kernelRxLocalTime) : mLastPacketRxLocalTime;
+ 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;
}
- // 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);
+ pkt.nak = 0;
+ pkt.clientTxLocalTime = request->clientTxLocalTime;
+ pkt.masterRxCommonTime = rxCommonTime;
+ pkt.masterTxCommonTime = txCommonTime;
} else {
- response.nak = htonl(1);
- response.clientTxLocalTime = htonl(0);
- response.masterRxCommonTime = htonl(0);
- response.masterTxCommonTime = htonl(0);
+ 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, &response, sizeof(response), 0,
+ mSocket, &buf, bufSz, 0,
reinterpret_cast<const sockaddr *>(&srcAddr),
sizeof(srcAddr));
if (sendBytes == -1) {
@@ -817,93 +818,112 @@ bool CommonTimeServer::handleSyncRequest(const SyncRequestPacket* request,
bool CommonTimeServer::handleSyncResponse(
const SyncResponsePacket* response,
- const sockaddr_in& srcAddr) {
+ const sockaddr_storage& srcAddr) {
if (mState != ICommonClock::STATE_CLIENT)
return true;
- if ((srcAddr.sin_addr.s_addr != mClient_MasterAddr.sin_addr.s_addr) ||
- (srcAddr.sin_port != mClient_MasterAddr.sin_port)) {
- uint32_t srcIP = ntohl(srcAddr.sin_addr.s_addr);
- uint32_t expectedIP = ntohl(mClient_MasterAddr.sin_addr.s_addr);
+ assert(mMasterEPValid);
+ if (!sockaddrMatch(srcAddr, mMasterEP, true)) {
+ char srcEP[64], expectedEP[64];
+ sockaddrToString(srcAddr, true, srcEP, sizeof(srcEP));
+ sockaddrToString(mMasterEP, true, expectedEP, sizeof(expectedEP));
LOGI("Dropping sync response from unexpected address."
- " Expected %u.%u.%u.%u:%hu"
- " Got %u.%u.%u.%u:%hu",
- ((expectedIP >> 24) & 0xFF), ((expectedIP >> 16) & 0xFF),
- ((expectedIP >> 8) & 0xFF), (expectedIP & 0xFF),
- ntohs(mClient_MasterAddr.sin_port),
- ((srcIP >> 24) & 0xFF), ((srcIP >> 16) & 0xFF),
- ((srcIP >> 8) & 0xFF), (srcIP & 0xFF),
- ntohs(srcAddr.sin_port));
+ " Expected %s Got %s", expectedEP, srcEP);
return true;
}
- if (ntohl(response->nak)) {
+ if (response->nak) {
// if our master is no longer accepting requests, then we need to find
// a new master
- return becomeRonin();
+ return becomeRonin("master NAK'ed");
}
mClient_SyncRequestPending = 0;
mClient_SyncRequestTimeouts = 0;
+ mClient_PacketRTTLog.logRX(response->clientTxLocalTime,
+ mLastPacketRxLocalTime);
bool result;
-
- if (!(mClient_SyncRespsRvcedFromCurMaster++)) {
+ 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 = 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 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;
- result = mClockRecovery.pushDisciplineEvent(avgLocal, avgCommon, rtt);
- if (result) {
- // indicate to listeners that we've synced to the common timeline
- notifyClockSync();
+ // 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.
+ int rttCommon = mCommonClock.localDurationToCommonDuration(rtt);
+ if (rttCommon > (static_cast<int64_t>(mPanicThresholdUsec) * 5)) {
+ LOGV("Dropping sync response with RTT of %lld uSec", rttCommon);
+ mClient_ExpiredSyncRespsRXedFromCurMaster++;
+ if (shouldPanicNotGettingGoodData())
+ return becomeInitial("RX panic, no good data");
} else {
- LOGE("Panic! Observed clock sync error is too high to tolerate,"
- " resetting state machine and starting over.");
- notifyClockSyncLoss();
- return becomeInitial();
+ result = mClockRecovery.pushDisciplineEvent(avgLocal, avgCommon, rtt);
+ mClient_LastGoodSyncRX = clientRxLocalTime;
+
+ 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("panic");
+ }
}
}
- mTimeoutMs = kClient_SyncRequestIntervalMs;
+ mCurTimeout.setTimeout(mSyncRequestIntervalMs);
return result;
}
bool CommonTimeServer::handleMasterAnnouncement(
const MasterAnnouncementPacket* packet,
- const sockaddr_in& srcAddr) {
- uint64_t newDeviceID = ntohq(packet->deviceID);
- uint32_t newTimelineID = ntohl(packet->timelineID);
+ const sockaddr_storage& srcAddr) {
+ uint64_t newDeviceID = packet->deviceID;
+ uint8_t newDevicePrio = packet->devicePriority;
+ uint64_t newTimelineID = packet->timelineID;
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, newTimelineID);
+ 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, mClient_MasterDeviceID))
- return becomeClient(srcAddr, newDeviceID, newTimelineID);
+ 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, mDeviceID))
- return becomeClient(srcAddr, newDeviceID, newTimelineID);
+ if (arbitrateMaster(newDeviceID, newDevicePrio,
+ mDeviceID, effectivePriority()))
+ return becomeClient(srcAddr, newDeviceID,
+ newDevicePrio, newTimelineID,
+ "heard master announcement");
}
return true;
@@ -912,85 +932,131 @@ bool CommonTimeServer::handleMasterAnnouncement(
bool CommonTimeServer::sendWhoIsMasterRequest() {
assert(mState == ICommonClock::STATE_INITIAL || mState == ICommonClock::STATE_RONIN);
- WhoIsMasterRequestPacket request;
- request.senderDeviceID = htonq(mDeviceID);
- request.timelineID = htonl(mTimelineID);
+ // 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;
+ }
- 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__);
+ 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) {
+ ssize_t sendBytes = sendto(
+ mSocket, buf, bufSz, 0,
+ reinterpret_cast<const sockaddr *>(&mMasterElectionEP),
+ sizeof(mMasterElectionEP));
+ if (sendBytes < 0)
+ LOGE("WhoIsMaster sendto failed (errno %d)", errno);
+ ret = true;
}
if (mState == ICommonClock::STATE_INITIAL) {
- mTimeoutMs = kInitial_WhoIsMasterTimeoutMs;
+ mCurTimeout.setTimeout(kInitial_WhoIsMasterTimeoutMs);
} else {
- mTimeoutMs = kRonin_WhoIsMasterTimeoutMs;
+ mCurTimeout.setTimeout(kRonin_WhoIsMasterTimeoutMs);
}
- return (sendBytes != -1);
+ 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);
- SyncRequestPacket request;
- request.timelineID = htonl(mTimelineID);
- request.clientTxLocalTime = htonq(mLocalClock.getLocalTime());
+ bool ret = false;
+ SyncRequestPacket pkt;
+ pkt.initHeader(mTimelineID, mSyncGroupID);
+ pkt.clientTxLocalTime = 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__);
+ 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)
+ LOGE("SyncRequest sendto failed (errno %d)", errno);
+ ret = true;
}
mClient_SyncsSentToCurMaster++;
- mTimeoutMs = kClient_SyncRequestTimeoutMs;
+ mCurTimeout.setTimeout(mSyncRequestIntervalMs);
mClient_SyncRequestPending = true;
- return (sendBytes != -1);
+
+ return ret;
}
bool CommonTimeServer::sendMasterAnnouncement() {
+ bool ret = false;
assert(mState == ICommonClock::STATE_MASTER);
- MasterAnnouncementPacket announce;
- announce.deviceID = htonq(mDeviceID);
- announce.timelineID = htonl(mTimelineID);
+ // 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;
+ }
- 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__);
+ 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) {
+ ssize_t sendBytes = sendto(
+ mSocket, buf, bufSz, 0,
+ reinterpret_cast<const sockaddr *>(&mMasterElectionEP),
+ sizeof(mMasterElectionEP));
+ if (sendBytes < 0)
+ LOGE("MasterAnnouncement sendto failed (errno %d)", errno);
+ ret = true;
}
- mTimeoutMs = kMaster_AnnouncementIntervalMs;
- return (sendBytes != -1);
+ mCurTimeout.setTimeout(mMasterAnnounceIntervalMs);
+ return ret;
}
-bool CommonTimeServer::becomeClient(const sockaddr_in& masterAddr,
- uint64_t masterDeviceID,
- uint32_t timelineID) {
- uint32_t newIP = ntohl(masterAddr.sin_addr.s_addr);
- uint32_t oldIP = ntohl(mClient_MasterAddr.sin_addr.s_addr);
- LOGI("%s --> CLIENT%s"
- " OldMaster: %016llx::%08x::%u.%u.%u.%u:%hu"
- " NewMaster: %016llx::%08x::%u.%u.%u.%u:%hu",
- stateToString(mState),
+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));
+
+ LOGI("%s --> CLIENT (%s) :%s"
+ " OldMaster: %02x-%014llx::%016llx::%s"
+ " NewMaster: %02x-%014llx::%016llx::%s",
+ stateToString(mState), cause,
(mTimelineID != timelineID) ? " (new timeline)" : "",
- mClient_MasterDeviceID, mTimelineID,
- ((oldIP >> 24) & 0xFF), ((oldIP >> 16) & 0xFF),
- ((oldIP >> 8) & 0xFF), (oldIP & 0xFF),
- ntohs(mClient_MasterAddr.sin_port),
- masterDeviceID, timelineID,
- ((newIP >> 24) & 0xFF), ((newIP >> 16) & 0xFF),
- ((newIP >> 8) & 0xFF), (newIP & 0xFF),
- ntohs(masterAddr.sin_port));
+ mClient_MasterDevicePriority, mClient_MasterDeviceID,
+ mTimelineID, oldEPStr,
+ masterDevicePriority, masterDeviceID,
+ timelineID, newEPStr);
if (mTimelineID != timelineID) {
// start following a new timeline
@@ -1002,25 +1068,26 @@ bool CommonTimeServer::becomeClient(const sockaddr_in& masterAddr,
mClockRecovery.reset(false, true);
}
- mClient_MasterAddr = masterAddr;
+ mMasterEP = masterEP;
+ mMasterEPValid = true;
+ setForceLowPriority(false);
+
mClient_MasterDeviceID = masterDeviceID;
- mClient_SyncRequestPending = 0;
- mClient_SyncRequestTimeouts = 0;
- mClient_SyncsSentToCurMaster = 0;
- mClient_SyncRespsRvcedFromCurMaster = 0;
+ 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((rand() % 100) * 1000);
+ usleep((lrand48() % 100) * 1000);
return sendSyncRequest();
}
-bool CommonTimeServer::becomeMaster() {
- uint32_t oldTimelineID = mTimelineID;
+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
@@ -1036,19 +1103,25 @@ bool CommonTimeServer::becomeMaster() {
notifyClockSync();
}
- LOGI("%s --> MASTER %s timeline %08x",
- stateToString(mState),
+ LOGI("%s --> MASTER (%s) : %s timeline %016llx",
+ stateToString(mState), cause,
(oldTimelineID == mTimelineID) ? "taking ownership of"
: "creating new",
mTimelineID);
+ memset(&mMasterEP, 0, sizeof(mMasterEP));
+ mMasterEPValid = false;
+ setForceLowPriority(false);
+ mClient_MasterDevicePriority = effectivePriority();
+ mClient_MasterDeviceID = mDeviceID;
mClockRecovery.reset(false, true);
+ resetSyncStats();
setState(ICommonClock::STATE_MASTER);
return sendMasterAnnouncement();
}
-bool CommonTimeServer::becomeRonin() {
+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
@@ -1057,47 +1130,51 @@ bool CommonTimeServer::becomeRonin() {
// 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.
- uint32_t oldIP = ntohl(mClient_MasterAddr.sin_addr.s_addr);
+
+ char oldEPStr[64];
+ sockaddrToString(mMasterEP, mMasterEPValid, oldEPStr, sizeof(oldEPStr));
+ memset(&mMasterEP, 0, sizeof(mMasterEP));
+ mMasterEPValid = false;
+
if (mCommonClock.isValid()) {
- LOGI("%s --> RONIN : lost track of previously valid timeline "
- "%016llx::%08x::%u.%u.%u.%u:%hu (%d TXed %d RXed)",
- stateToString(mState),
- mClient_MasterDeviceID, mTimelineID,
- ((oldIP >> 24) & 0xFF), ((oldIP >> 16) & 0xFF),
- ((oldIP >> 8) & 0xFF), (oldIP & 0xFF),
- ntohs(mClient_MasterAddr.sin_port),
+ LOGI("%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_SyncRespsRvcedFromCurMaster);
+ mClient_SyncRespsRXedFromCurMaster,
+ mClient_ExpiredSyncRespsRXedFromCurMaster);
mRonin_WhoIsMasterRequestTimeouts = 0;
setState(ICommonClock::STATE_RONIN);
return sendWhoIsMasterRequest();
} else {
- LOGI("%s --> INITIAL : never synced timeline "
- "%016llx::%08x::%u.%u.%u.%u:%hu (%d TXed %d RXed)",
- stateToString(mState),
- mClient_MasterDeviceID, mTimelineID,
- ((oldIP >> 24) & 0xFF), ((oldIP >> 16) & 0xFF),
- ((oldIP >> 8) & 0xFF), (oldIP & 0xFF),
- ntohs(mClient_MasterAddr.sin_port),
+ LOGI("%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_SyncRespsRvcedFromCurMaster);
+ mClient_SyncRespsRXedFromCurMaster,
+ mClient_ExpiredSyncRespsRXedFromCurMaster);
- return becomeInitial();
+ return becomeInitial("ronin, no timeline");
}
}
-bool CommonTimeServer::becomeWaitForElection() {
- LOGI("%s --> WAIT_FOR_ELECTION : dropping out of election, waiting %d mSec"
- " for completion.", stateToString(mState), kWaitForElection_TimeoutMs);
+bool CommonTimeServer::becomeWaitForElection(const char* cause) {
+ LOGI("%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);
- mTimeoutMs = kWaitForElection_TimeoutMs;
+ mCurTimeout.setTimeout(kWaitForElection_TimeoutMs);
return true;
}
-bool CommonTimeServer::becomeInitial() {
- LOGI("Entering INITIAL, total reset.");
+bool CommonTimeServer::becomeInitial(const char* cause) {
+ LOGI("Entering INITIAL (%s), total reset.", cause);
setState(ICommonClock::STATE_INITIAL);
@@ -1105,17 +1182,17 @@ bool CommonTimeServer::becomeInitial() {
mClockRecovery.reset(true, true);
// reset internal state bookkeeping.
- mTimeoutMs = kInfiniteTimeout;
+ mCurTimeout.setTimeout(kInfiniteTimeout);
+ memset(&mMasterEP, 0, sizeof(mMasterEP));
+ mMasterEPValid = false;
mLastPacketRxLocalTime = 0;
mTimelineID = ICommonClock::kInvalidTimelineID;
mClockSynced = false;
mInitial_WhoIsMasterRequestTimeouts = 0;
mClient_MasterDeviceID = 0;
- mClient_SyncsSentToCurMaster = 0;
- mClient_SyncRespsRvcedFromCurMaster = 0;
- mClient_SyncRequestPending = false;
- mClient_SyncRequestTimeouts = 0;
+ mClient_MasterDevicePriority = 0;
mRonin_WhoIsMasterRequestTimeouts = 0;
+ resetSyncStats();
// send the first request to discover the master
return sendWhoIsMasterRequest();
@@ -1157,18 +1234,145 @@ const char* CommonTimeServer::stateToString(ICommonClock::State s) {
}
}
-} // namespace android
+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;
+ }
+}
-int main(int argc, char *argv[]) {
- using namespace android;
+void CommonTimeServer::TimeoutHelper::setTimeout(int msec) {
+ mTimeoutValid = (msec >= 0);
+ if (mTimeoutValid)
+ mEndTime = systemTime() +
+ (static_cast<nsecs_t>(msec) * 1000000);
+}
+
+int CommonTimeServer::TimeoutHelper::msecTillTimeout() {
+ if (!mTimeoutValid)
+ return kInfiniteTimeout;
- sp<CommonTimeServer> service = new CommonTimeServer();
- if (service == NULL)
- return 1;
+ nsecs_t now = systemTime();
+ if (now >= mEndTime)
+ return 0;
- ProcessState::self()->startThreadPool();
- service->run("CommonTimeServer", ANDROID_PRIORITY_NORMAL);
+ uint64_t deltaMsec = (((mEndTime - now) + 999999) / 1000000);
- IPCThreadState::self()->joinThreadPool();
- return 0;
+ if (deltaMsec > static_cast<uint64_t>(std::numeric_limits<int>::max()))
+ return std::numeric_limits<int>::max();
+
+ return static_cast<int>(deltaMsec);
}
+
+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 >= kNoGoodDataPanicThreshold)
+ 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
diff --git a/services/common_time/common_time_server.h b/services/common_time/common_time_server.h
new file mode 100644
index 0000000..d4d07c9
--- /dev/null
+++ b/services/common_time/common_time_server.h
@@ -0,0 +1,330 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_COMMON_TIME_SERVER_H
+#define ANDROID_COMMON_TIME_SERVER_H
+
+#include <arpa/inet.h>
+#include <stdint.h>
+#include <linux/socket.h>
+
+#include <common_time/ICommonClock.h>
+#include <common_time/local_clock.h>
+#include <utils/String8.h>
+
+#include "clock_recovery.h"
+#include "common_clock.h"
+#include "common_time_server_packets.h"
+
+#define RTT_LOG_SIZE 30
+
+namespace android {
+
+class CommonClockService;
+class CommonTimeConfigService;
+
+/***** time service implementation *****/
+
+class CommonTimeServer : public Thread {
+ public:
+ CommonTimeServer();
+ ~CommonTimeServer();
+
+ bool startServices();
+
+ // Common Clock API methods
+ CommonClock& getCommonClock() { return mCommonClock; }
+ LocalClock& getLocalClock() { return mLocalClock; }
+ uint64_t getTimelineID();
+ int32_t getEstimatedError();
+ ICommonClock::State getState();
+ status_t getMasterAddr(struct sockaddr_storage* addr);
+ status_t isCommonTimeValid(bool* valid, uint32_t* timelineID);
+
+ // Config API methods
+ status_t getMasterElectionPriority(uint8_t *priority);
+ status_t setMasterElectionPriority(uint8_t priority);
+ status_t getMasterElectionEndpoint(struct sockaddr_storage *addr);
+ status_t setMasterElectionEndpoint(const struct sockaddr_storage *addr);
+ status_t getMasterElectionGroupId(uint64_t *id);
+ status_t setMasterElectionGroupId(uint64_t id);
+ status_t getInterfaceBinding(String8& ifaceName);
+ status_t setInterfaceBinding(const String8& ifaceName);
+ status_t getMasterAnnounceInterval(int *interval);
+ status_t setMasterAnnounceInterval(int interval);
+ status_t getClientSyncInterval(int *interval);
+ status_t setClientSyncInterval(int interval);
+ status_t getPanicThreshold(int *threshold);
+ status_t setPanicThreshold(int threshold);
+ status_t getAutoDisable(bool *autoDisable);
+ status_t setAutoDisable(bool autoDisable);
+ status_t forceNetworklessMasterMode();
+
+ // Method used by the CommonClockService to notify the core service about
+ // changes in the number of active common clock clients.
+ void reevaluateAutoDisableState(bool commonClockHasClients);
+
+ status_t dumpClockInterface(int fd, const Vector<String16>& args,
+ size_t activeClients);
+ status_t dumpConfigInterface(int fd, const Vector<String16>& args);
+
+ private:
+ class PacketRTTLog {
+ public:
+ PacketRTTLog() {
+ resetLog();
+ }
+
+ void resetLog() {
+ wrPtr = 0;
+ logFull = 0;
+ }
+
+ void logTX(int64_t txTime);
+ void logRX(int64_t txTime, int64_t rxTime);
+ void dumpLog(int fd, const CommonClock& cclk);
+
+ private:
+ uint32_t wrPtr;
+ bool logFull;
+ int64_t txTimes[RTT_LOG_SIZE];
+ int64_t rxTimes[RTT_LOG_SIZE];
+ };
+
+ class TimeoutHelper {
+ public:
+ TimeoutHelper() : mTimeoutValid(false) { }
+
+ void setTimeout(int msec);
+ int msecTillTimeout();
+
+ private:
+ bool mTimeoutValid;
+ nsecs_t mEndTime;
+ };
+
+ bool threadLoop();
+
+ bool runStateMachine_l();
+ bool setupSocket_l();
+
+ void assignTimelineID();
+ bool assignDeviceID();
+
+ static bool arbitrateMaster(uint64_t deviceID1, uint8_t devicePrio1,
+ uint64_t deviceID2, uint8_t devicePrio2);
+
+ bool handlePacket();
+ bool handleWhoIsMasterRequest (const WhoIsMasterRequestPacket* request,
+ const sockaddr_storage& srcAddr);
+ bool handleWhoIsMasterResponse(const WhoIsMasterResponsePacket* response,
+ const sockaddr_storage& srcAddr);
+ bool handleSyncRequest (const SyncRequestPacket* request,
+ const sockaddr_storage& srcAddr);
+ bool handleSyncResponse (const SyncResponsePacket* response,
+ const sockaddr_storage& srcAddr);
+ bool handleMasterAnnouncement (const MasterAnnouncementPacket* packet,
+ const sockaddr_storage& srcAddr);
+
+ bool handleTimeout();
+ bool handleTimeoutInitial();
+ bool handleTimeoutClient();
+ bool handleTimeoutMaster();
+ bool handleTimeoutRonin();
+ bool handleTimeoutWaitForElection();
+
+ bool sendWhoIsMasterRequest();
+ bool sendSyncRequest();
+ bool sendMasterAnnouncement();
+
+ bool becomeClient(const sockaddr_storage& masterAddr,
+ uint64_t masterDeviceID,
+ uint8_t masterDevicePriority,
+ uint64_t timelineID,
+ const char* cause);
+ bool becomeMaster(const char* cause);
+ bool becomeRonin(const char* cause);
+ bool becomeWaitForElection(const char* cause);
+ bool becomeInitial(const char* cause);
+
+ void notifyClockSync();
+ void notifyClockSyncLoss();
+
+ ICommonClock::State mState;
+ void setState(ICommonClock::State s);
+
+ void clearPendingWakeupEvents_l();
+ void wakeupThread_l();
+ void cleanupSocket_l();
+ void shutdownThread();
+
+ inline uint8_t effectivePriority() const {
+ return (mMasterPriority & 0x7F) |
+ (mForceLowPriority ? 0x00 : 0x80);
+ }
+
+ inline bool shouldAutoDisable() const {
+ return (mAutoDisable && !mCommonClockHasClients);
+ }
+
+ inline void resetSyncStats() {
+ mClient_SyncRequestPending = false;
+ mClient_SyncRequestTimeouts = 0;
+ mClient_SyncsSentToCurMaster = 0;
+ mClient_SyncRespsRXedFromCurMaster = 0;
+ mClient_ExpiredSyncRespsRXedFromCurMaster = 0;
+ mClient_FirstSyncTX = 0;
+ mClient_LastGoodSyncRX = 0;
+ mClient_PacketRTTLog.resetLog();
+ }
+
+ bool shouldPanicNotGettingGoodData();
+
+ // Helper to keep track of the state machine's current timeout
+ TimeoutHelper mCurTimeout;
+
+ // common clock, local clock abstraction, and clock recovery loop
+ CommonClock mCommonClock;
+ LocalClock mLocalClock;
+ ClockRecoveryLoop mClockRecovery;
+
+ // implementation of ICommonClock
+ sp<CommonClockService> mICommonClock;
+
+ // implementation of ICommonTimeConfig
+ sp<CommonTimeConfigService> mICommonTimeConfig;
+
+ // UDP socket for the time sync protocol
+ int mSocket;
+
+ // eventfd used to wakeup the work thread in response to configuration
+ // changes.
+ int mWakeupThreadFD;
+
+ // timestamp captured when a packet is received
+ int64_t mLastPacketRxLocalTime;
+
+ // ID of the timeline that this device is following
+ uint64_t mTimelineID;
+
+ // flag for whether the clock has been synced to a timeline
+ bool mClockSynced;
+
+ // flag used to indicate that clients should be considered to be lower
+ // priority than all of their peers during elections. This flag is set and
+ // cleared by the state machine. It is set when the client joins a new
+ // network. If the client had been a master in the old network (or an
+ // isolated master with no network connectivity) it should defer to any
+ // masters which may already be on the network. It will be cleared whenever
+ // the state machine transitions to the master state.
+ bool mForceLowPriority;
+ inline void setForceLowPriority(bool val) {
+ mForceLowPriority = val;
+ if (mState == ICommonClock::STATE_MASTER)
+ mClient_MasterDevicePriority = effectivePriority();
+ }
+
+ // Lock to synchronize access to internal state and configuration.
+ Mutex mLock;
+
+ // Flag updated by the common clock service to indicate that it does or does
+ // not currently have registered clients. When the the auto disable flag is
+ // cleared on the common time service, the service will participate in
+ // network synchronization whenever it has a valid network interface to bind
+ // to. When the auto disable flag is set on the common time service, it
+ // will only participate in network synchronization when it has both a valid
+ // interface AND currently active common clock clients.
+ bool mCommonClockHasClients;
+
+ // Configuration info
+ struct sockaddr_storage mMasterElectionEP; // Endpoint over which we conduct master election
+ String8 mBindIface; // Endpoint for the service to bind to.
+ bool mBindIfaceValid; // whether or not the bind Iface is valid.
+ bool mBindIfaceDirty; // whether or not the bind Iface is valid.
+ struct sockaddr_storage mMasterEP; // Endpoint of our current master (if any)
+ bool mMasterEPValid;
+ uint64_t mDeviceID; // unique ID of this device
+ uint64_t mSyncGroupID; // synchronization group ID of this device.
+ uint8_t mMasterPriority; // Priority of this device in master election.
+ uint32_t mMasterAnnounceIntervalMs;
+ uint32_t mSyncRequestIntervalMs;
+ uint32_t mPanicThresholdUsec;
+ bool mAutoDisable;
+
+ // Config defaults.
+ static const char* kDefaultMasterElectionAddr;
+ static const uint16_t kDefaultMasterElectionPort;
+ static const uint64_t kDefaultSyncGroupID;
+ static const uint8_t kDefaultMasterPriority;
+ static const uint32_t kDefaultMasterAnnounceIntervalMs;
+ static const uint32_t kDefaultSyncRequestIntervalMs;
+ static const uint32_t kDefaultPanicThresholdUsec;
+ static const bool kDefaultAutoDisable;
+
+ // Priority mask and shift fields.
+ static const uint64_t kDeviceIDMask;
+ static const uint8_t kDevicePriorityMask;
+ static const uint8_t kDevicePriorityHiLowBit;
+ static const uint32_t kDevicePriorityShift;
+
+ // Unconfgurable constants
+ static const int kSetupRetryTimeout;
+ static const int64_t kNoGoodDataPanicThreshold;
+
+ /*** 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 ***/
+ uint64_t mClient_MasterDeviceID;
+ uint8_t mClient_MasterDevicePriority;
+ bool mClient_SyncRequestPending;
+ int mClient_SyncRequestTimeouts;
+ uint32_t mClient_SyncsSentToCurMaster;
+ uint32_t mClient_SyncRespsRXedFromCurMaster;
+ uint32_t mClient_ExpiredSyncRespsRXedFromCurMaster;
+ int64_t mClient_FirstSyncTX;
+ int64_t mClient_LastGoodSyncRX;
+ PacketRTTLog mClient_PacketRTTLog;
+ static const int kClient_NumSyncRequestRetries;
+
+
+ /*** status while in the Master state ***/
+ static const uint32_t kDefaultMaster_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 int kInfiniteTimeout;
+
+ static const char* stateToString(ICommonClock::State s);
+ static void sockaddrToString(const sockaddr_storage& addr, bool addrValid,
+ char* buf, size_t bufLen);
+ static bool sockaddrMatch(const sockaddr_storage& a1,
+ const sockaddr_storage& a2,
+ bool matchAddressOnly);
+};
+
+} // namespace android
+
+#endif // ANDROID_COMMON_TIME_SERVER_H
+
diff --git a/services/common_time/common_time_server_api.cpp b/services/common_time/common_time_server_api.cpp
new file mode 100644
index 0000000..657a382
--- /dev/null
+++ b/services/common_time/common_time_server_api.cpp
@@ -0,0 +1,435 @@
+/*
+ * 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 <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+
+#include "common_time_server.h"
+
+namespace android {
+
+//
+// Clock API
+//
+uint64_t CommonTimeServer::getTimelineID() {
+ AutoMutex _lock(&mLock);
+ return mTimelineID;
+}
+
+ICommonClock::State CommonTimeServer::getState() {
+ AutoMutex _lock(&mLock);
+ return mState;
+}
+
+status_t CommonTimeServer::getMasterAddr(struct sockaddr_storage* addr) {
+ AutoMutex _lock(&mLock);
+ if (mMasterEPValid) {
+ memcpy(addr, &mMasterEP, sizeof(*addr));
+ return OK;
+ }
+
+ return UNKNOWN_ERROR;
+}
+
+int32_t CommonTimeServer::getEstimatedError() {
+ AutoMutex _lock(&mLock);
+
+ if (ICommonClock::STATE_MASTER == mState)
+ return 0;
+
+ if (!mClockSynced)
+ return ICommonClock::kErrorEstimateUnknown;
+
+ return mClockRecovery.getLastErrorEstimate();
+}
+
+status_t CommonTimeServer::isCommonTimeValid(bool* valid,
+ uint32_t* timelineID) {
+ AutoMutex _lock(&mLock);
+ *valid = mCommonClock.isValid();
+ *timelineID = mTimelineID;
+ return OK;
+}
+
+//
+// Config API
+//
+status_t CommonTimeServer::getMasterElectionPriority(uint8_t *priority) {
+ AutoMutex _lock(&mLock);
+ *priority = mMasterPriority;
+ return OK;
+}
+
+status_t CommonTimeServer::setMasterElectionPriority(uint8_t priority) {
+ AutoMutex _lock(&mLock);
+
+ if (priority > 0x7F)
+ return BAD_VALUE;
+
+ mMasterPriority = priority;
+ return OK;
+}
+
+status_t CommonTimeServer::getMasterElectionEndpoint(
+ struct sockaddr_storage *addr) {
+ AutoMutex _lock(&mLock);
+ memcpy(addr, &mMasterElectionEP, sizeof(*addr));
+ return OK;
+}
+
+status_t CommonTimeServer::setMasterElectionEndpoint(
+ const struct sockaddr_storage *addr) {
+ AutoMutex _lock(&mLock);
+
+ if (!addr)
+ return BAD_VALUE;
+
+ // TODO: add proper support for IPv6
+ if (addr->ss_family != AF_INET)
+ return BAD_VALUE;
+
+ // Only multicast and broadcast endpoints with explicit ports are allowed.
+ uint16_t ipv4Port = ntohs(
+ reinterpret_cast<const struct sockaddr_in*>(addr)->sin_port);
+ if (!ipv4Port)
+ return BAD_VALUE;
+
+ uint32_t ipv4Addr = ntohl(
+ reinterpret_cast<const struct sockaddr_in*>(addr)->sin_addr.s_addr);
+ if ((ipv4Addr != 0xFFFFFFFF) && (0xE0000000 != (ipv4Addr & 0xF0000000)))
+ return BAD_VALUE;
+
+ memcpy(&mMasterElectionEP, addr, sizeof(mMasterElectionEP));
+
+ // Force a rebind in order to change election enpoints.
+ mBindIfaceDirty = true;
+ wakeupThread_l();
+ return OK;
+}
+
+status_t CommonTimeServer::getMasterElectionGroupId(uint64_t *id) {
+ AutoMutex _lock(&mLock);
+ *id = mSyncGroupID;
+ return OK;
+}
+
+status_t CommonTimeServer::setMasterElectionGroupId(uint64_t id) {
+ AutoMutex _lock(&mLock);
+ mSyncGroupID = id;
+ return OK;
+}
+
+status_t CommonTimeServer::getInterfaceBinding(String8& ifaceName) {
+ AutoMutex _lock(&mLock);
+ if (!mBindIfaceValid)
+ return INVALID_OPERATION;
+ ifaceName = mBindIface;
+ return OK;
+}
+
+status_t CommonTimeServer::setInterfaceBinding(const String8& ifaceName) {
+ AutoMutex _lock(&mLock);
+
+ mBindIfaceDirty = true;
+ if (ifaceName.size()) {
+ mBindIfaceValid = true;
+ mBindIface = ifaceName;
+ } else {
+ mBindIfaceValid = false;
+ mBindIface.clear();
+ }
+
+ wakeupThread_l();
+ return OK;
+}
+
+status_t CommonTimeServer::getMasterAnnounceInterval(int *interval) {
+ AutoMutex _lock(&mLock);
+ *interval = mMasterAnnounceIntervalMs;
+ return OK;
+}
+
+status_t CommonTimeServer::setMasterAnnounceInterval(int interval) {
+ AutoMutex _lock(&mLock);
+
+ if (interval > (6 *3600000)) // Max interval is once every 6 hrs
+ return BAD_VALUE;
+
+ if (interval < 500) // Min interval is once per 0.5 seconds
+ return BAD_VALUE;
+
+ mMasterAnnounceIntervalMs = interval;
+ if (ICommonClock::STATE_MASTER == mState) {
+ int pendingTimeout = mCurTimeout.msecTillTimeout();
+ if ((kInfiniteTimeout == pendingTimeout) ||
+ (pendingTimeout > interval)) {
+ mCurTimeout.setTimeout(mMasterAnnounceIntervalMs);
+ wakeupThread_l();
+ }
+ }
+
+ return OK;
+}
+
+status_t CommonTimeServer::getClientSyncInterval(int *interval) {
+ AutoMutex _lock(&mLock);
+ *interval = mSyncRequestIntervalMs;
+ return OK;
+}
+
+status_t CommonTimeServer::setClientSyncInterval(int interval) {
+ AutoMutex _lock(&mLock);
+
+ if (interval > (3600000)) // Max interval is once every 60 min
+ return BAD_VALUE;
+
+ if (interval < 250) // Min interval is once per 0.25 seconds
+ return BAD_VALUE;
+
+ mSyncRequestIntervalMs = interval;
+ if (ICommonClock::STATE_CLIENT == mState) {
+ int pendingTimeout = mCurTimeout.msecTillTimeout();
+ if ((kInfiniteTimeout == pendingTimeout) ||
+ (pendingTimeout > interval)) {
+ mCurTimeout.setTimeout(mSyncRequestIntervalMs);
+ wakeupThread_l();
+ }
+ }
+
+ return OK;
+}
+
+status_t CommonTimeServer::getPanicThreshold(int *threshold) {
+ AutoMutex _lock(&mLock);
+ *threshold = mPanicThresholdUsec;
+ return OK;
+}
+
+status_t CommonTimeServer::setPanicThreshold(int threshold) {
+ AutoMutex _lock(&mLock);
+
+ if (threshold < 1000) // Min threshold is 1mSec
+ return BAD_VALUE;
+
+ mPanicThresholdUsec = threshold;
+ return OK;
+}
+
+status_t CommonTimeServer::getAutoDisable(bool *autoDisable) {
+ AutoMutex _lock(&mLock);
+ *autoDisable = mAutoDisable;
+ return OK;
+}
+
+status_t CommonTimeServer::setAutoDisable(bool autoDisable) {
+ AutoMutex _lock(&mLock);
+ mAutoDisable = autoDisable;
+ wakeupThread_l();
+ return OK;
+}
+
+status_t CommonTimeServer::forceNetworklessMasterMode() {
+ AutoMutex _lock(&mLock);
+
+ // Can't force networkless master mode if we are currently bound to a
+ // network.
+ if (mSocket >= 0)
+ return INVALID_OPERATION;
+
+ becomeMaster("force networkless");
+
+ return OK;
+}
+
+void CommonTimeServer::reevaluateAutoDisableState(bool commonClockHasClients) {
+ AutoMutex _lock(&mLock);
+ bool needWakeup = (mAutoDisable && mMasterEPValid &&
+ (commonClockHasClients != mCommonClockHasClients));
+
+ mCommonClockHasClients = commonClockHasClients;
+
+ if (needWakeup) {
+ LOGI("Waking up service, auto-disable is engaged and service now has%s"
+ " clients", mCommonClockHasClients ? "" : " no");
+ wakeupThread_l();
+ }
+}
+
+#define dump_printf(a, b...) do { \
+ int res; \
+ res = snprintf(buffer, sizeof(buffer), a, b); \
+ buffer[sizeof(buffer) - 1] = 0; \
+ if (res > 0) \
+ write(fd, buffer, res); \
+} while (0)
+#define checked_percentage(a, b) ((0 == b) ? 0.0f : ((100.0f * a) / b))
+
+status_t CommonTimeServer::dumpClockInterface(int fd,
+ const Vector<String16>& args,
+ size_t activeClients) {
+ AutoMutex _lock(&mLock);
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+
+ if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ snprintf(buffer, SIZE, "Permission Denial: "
+ "can't dump CommonClockService from pid=%d, uid=%d\n",
+ IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ write(fd, buffer, strlen(buffer));
+ } else {
+ int64_t commonTime;
+ int64_t localTime;
+ bool synced;
+ char maStr[64];
+
+ localTime = mLocalClock.getLocalTime();
+ synced = (OK == mCommonClock.localToCommon(localTime, &commonTime));
+ sockaddrToString(mMasterEP, mMasterEPValid, maStr, sizeof(maStr));
+
+ dump_printf("Common Clock Service Status\nLocal time : %lld\n",
+ localTime);
+
+ if (synced)
+ dump_printf("Common time : %lld\n", commonTime);
+ else
+ dump_printf("Common time : %s\n", "not synced");
+
+ dump_printf("Timeline ID : %016llx\n", mTimelineID);
+ dump_printf("State : %s\n", stateToString(mState));
+ dump_printf("Master Addr : %s\n", maStr);
+
+
+ if (synced) {
+ int32_t est = (ICommonClock::STATE_MASTER != mState)
+ ? mClockRecovery.getLastErrorEstimate()
+ : 0;
+ dump_printf("Error Est. : %.3f msec\n",
+ static_cast<float>(est) / 1000.0);
+ } else {
+ dump_printf("Error Est. : %s\n", "unknown");
+ }
+
+ dump_printf("Syncs TXes : %u\n", mClient_SyncsSentToCurMaster);
+ dump_printf("Syncs RXes : %u (%.2f%%)\n",
+ mClient_SyncRespsRXedFromCurMaster,
+ checked_percentage(
+ mClient_SyncRespsRXedFromCurMaster,
+ mClient_SyncsSentToCurMaster));
+ dump_printf("RXs Expired : %u (%.2f%%)\n",
+ mClient_ExpiredSyncRespsRXedFromCurMaster,
+ checked_percentage(
+ mClient_ExpiredSyncRespsRXedFromCurMaster,
+ mClient_SyncsSentToCurMaster));
+
+ if (!mClient_LastGoodSyncRX) {
+ dump_printf("Last Good RX : %s\n", "unknown");
+ } else {
+ int64_t localDelta, usecDelta;
+ localDelta = localTime - mClient_LastGoodSyncRX;
+ usecDelta = mCommonClock.localDurationToCommonDuration(localDelta);
+ dump_printf("Last Good RX : %lld uSec ago\n", usecDelta);
+ }
+
+ dump_printf("Active Clients : %u\n", activeClients);
+ mClient_PacketRTTLog.dumpLog(fd, mCommonClock);
+ }
+
+ return NO_ERROR;
+}
+
+status_t CommonTimeServer::dumpConfigInterface(int fd,
+ const Vector<String16>& args) {
+ AutoMutex _lock(&mLock);
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+
+ if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ snprintf(buffer, SIZE, "Permission Denial: "
+ "can't dump CommonTimeConfigService from pid=%d, uid=%d\n",
+ IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ write(fd, buffer, strlen(buffer));
+ } else {
+ char meStr[64];
+
+ sockaddrToString(mMasterElectionEP, true, meStr, sizeof(meStr));
+
+ dump_printf("Common Time Config Service Status\n"
+ "Bound Interface : %s\n",
+ mBindIfaceValid ? mBindIface.string() : "<unbound>");
+ dump_printf("Master Election Endpoint : %s\n", meStr);
+ dump_printf("Master Election Group ID : %016llx\n", mSyncGroupID);
+ dump_printf("Master Announce Interval : %d mSec\n",
+ mMasterAnnounceIntervalMs);
+ dump_printf("Client Sync Interval : %d mSec\n",
+ mSyncRequestIntervalMs);
+ dump_printf("Panic Threshold : %d uSec\n",
+ mPanicThresholdUsec);
+ dump_printf("Base ME Prio : 0x%02x\n",
+ static_cast<uint32_t>(mMasterPriority));
+ dump_printf("Effective ME Prio : 0x%02x\n",
+ static_cast<uint32_t>(effectivePriority()));
+ dump_printf("Auto Disable Allowed : %s\n",
+ mAutoDisable ? "yes" : "no");
+ dump_printf("Auto Disable Engaged : %s\n",
+ shouldAutoDisable() ? "yes" : "no");
+ }
+
+ return NO_ERROR;
+}
+
+void CommonTimeServer::PacketRTTLog::dumpLog(int fd, const CommonClock& cclk) {
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ uint32_t avail = !logFull ? wrPtr : RTT_LOG_SIZE;
+
+ if (!avail)
+ return;
+
+ dump_printf("\nPacket Log (%d entries)\n", avail);
+
+ uint32_t ndx = 0;
+ uint32_t i = logFull ? wrPtr : 0;
+ do {
+ if (rxTimes[i]) {
+ int64_t delta = rxTimes[i] - txTimes[i];
+ int64_t deltaUsec = cclk.localDurationToCommonDuration(delta);
+ dump_printf("pkt[%2d] : localTX %12lld localRX %12lld "
+ "(%.3f msec RTT)\n",
+ ndx, txTimes[i], rxTimes[i],
+ static_cast<float>(deltaUsec) / 1000.0);
+ } else {
+ dump_printf("pkt[%2d] : localTX %12lld localRX never\n",
+ ndx, txTimes[i]);
+ }
+ i = (i + 1) % RTT_LOG_SIZE;
+ ndx++;
+ } while (i != wrPtr);
+}
+
+#undef dump_printf
+#undef checked_percentage
+
+} // namespace android
diff --git a/services/common_time/common_time_server_packets.cpp b/services/common_time/common_time_server_packets.cpp
new file mode 100644
index 0000000..9833c37
--- /dev/null
+++ b/services/common_time/common_time_server_packets.cpp
@@ -0,0 +1,293 @@
+/*
+ * 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 <stdint.h>
+
+#include "common_time_server_packets.h"
+
+namespace android {
+
+const uint32_t TimeServicePacketHeader::kMagic =
+ (static_cast<uint32_t>('c') << 24) |
+ (static_cast<uint32_t>('c') << 16) |
+ (static_cast<uint32_t>('l') << 8) |
+ static_cast<uint32_t>('k');
+
+const uint16_t TimeServicePacketHeader::kCurVersion = 1;
+
+#define SERIALIZE_FIELD(field_name, type, converter) \
+ do { \
+ if ((offset + sizeof(field_name)) > length) \
+ return -1; \
+ *((type*)(data + offset)) = converter(field_name); \
+ offset += sizeof(field_name); \
+ } while (0)
+#define SERIALIZE_INT16(field_name) SERIALIZE_FIELD(field_name, int16_t, htons)
+#define SERIALIZE_INT32(field_name) SERIALIZE_FIELD(field_name, int32_t, htonl)
+#define SERIALIZE_INT64(field_name) SERIALIZE_FIELD(field_name, int64_t, htonq)
+
+#define DESERIALIZE_FIELD(field_name, type, converter) \
+ do { \
+ if ((offset + sizeof(field_name)) > length) \
+ return -1; \
+ field_name = converter(*((type*)(data + offset))); \
+ offset += sizeof(field_name); \
+ } while (0)
+#define DESERIALIZE_INT16(field_name) DESERIALIZE_FIELD(field_name, int16_t, ntohs)
+#define DESERIALIZE_INT32(field_name) DESERIALIZE_FIELD(field_name, int32_t, ntohl)
+#define DESERIALIZE_INT64(field_name) DESERIALIZE_FIELD(field_name, int64_t, ntohq)
+
+#define kDevicePriorityShift 56
+#define kDeviceIDMask ((static_cast<uint64_t>(1) << kDevicePriorityShift) - 1)
+
+inline uint64_t packDeviceID(uint64_t devID, uint8_t prio) {
+ return (devID & kDeviceIDMask) |
+ (static_cast<uint64_t>(prio) << kDevicePriorityShift);
+}
+
+inline uint64_t unpackDeviceID(uint64_t packed) {
+ return (packed & kDeviceIDMask);
+}
+
+inline uint8_t unpackDevicePriority(uint64_t packed) {
+ return static_cast<uint8_t>(packed >> kDevicePriorityShift);
+}
+
+ssize_t TimeServicePacketHeader::serializeHeader(uint8_t* data,
+ uint32_t length) {
+ ssize_t offset = 0;
+ int16_t pktType = static_cast<int16_t>(packetType);
+ SERIALIZE_INT32(magic);
+ SERIALIZE_INT16(version);
+ SERIALIZE_INT16(pktType);
+ SERIALIZE_INT64(timelineID);
+ SERIALIZE_INT64(syncGroupID);
+ return offset;
+}
+
+ssize_t TimeServicePacketHeader::deserializeHeader(const uint8_t* data,
+ uint32_t length) {
+ ssize_t offset = 0;
+ int16_t tmp;
+ DESERIALIZE_INT32(magic);
+ DESERIALIZE_INT16(version);
+ DESERIALIZE_INT16(tmp);
+ DESERIALIZE_INT64(timelineID);
+ DESERIALIZE_INT64(syncGroupID);
+ packetType = static_cast<TimeServicePacketType>(tmp);
+ return offset;
+}
+
+ssize_t TimeServicePacketHeader::serializePacket(uint8_t* data,
+ uint32_t length) {
+ ssize_t ret, tmp;
+
+ ret = serializeHeader(data, length);
+ if (ret < 0)
+ return ret;
+
+ data += ret;
+ length -= ret;
+
+ switch (packetType) {
+ case TIME_PACKET_WHO_IS_MASTER_REQUEST:
+ tmp =((WhoIsMasterRequestPacket*)(this))->serializePacket(data,
+ length);
+ break;
+ case TIME_PACKET_WHO_IS_MASTER_RESPONSE:
+ tmp =((WhoIsMasterResponsePacket*)(this))->serializePacket(data,
+ length);
+ break;
+ case TIME_PACKET_SYNC_REQUEST:
+ tmp =((SyncRequestPacket*)(this))->serializePacket(data, length);
+ break;
+ case TIME_PACKET_SYNC_RESPONSE:
+ tmp =((SyncResponsePacket*)(this))->serializePacket(data, length);
+ break;
+ case TIME_PACKET_MASTER_ANNOUNCEMENT:
+ tmp =((MasterAnnouncementPacket*)(this))->serializePacket(data,
+ length);
+ break;
+ default:
+ return -1;
+ }
+
+ if (tmp < 0)
+ return tmp;
+
+ return ret + tmp;
+}
+
+ssize_t UniversalTimeServicePacket::deserializePacket(
+ const uint8_t* data,
+ uint32_t length,
+ uint64_t expectedSyncGroupID) {
+ ssize_t ret;
+ TimeServicePacketHeader* header;
+ if (length < 8)
+ return -1;
+
+ packetType = ntohs(*((uint16_t*)(data + 6)));
+ switch (packetType) {
+ case TIME_PACKET_WHO_IS_MASTER_REQUEST:
+ ret = p.who_is_master_request.deserializePacket(data, length);
+ header = &p.who_is_master_request;
+ break;
+ case TIME_PACKET_WHO_IS_MASTER_RESPONSE:
+ ret = p.who_is_master_response.deserializePacket(data, length);
+ header = &p.who_is_master_response;
+ break;
+ case TIME_PACKET_SYNC_REQUEST:
+ ret = p.sync_request.deserializePacket(data, length);
+ header = &p.sync_request;
+ break;
+ case TIME_PACKET_SYNC_RESPONSE:
+ ret = p.sync_response.deserializePacket(data, length);
+ header = &p.sync_response;
+ break;
+ case TIME_PACKET_MASTER_ANNOUNCEMENT:
+ ret = p.master_announcement.deserializePacket(data, length);
+ header = &p.master_announcement;
+ break;
+ default:
+ return -1;
+ }
+
+ if ((ret >= 0) && !header->checkPacket(expectedSyncGroupID))
+ ret = -1;
+
+ return ret;
+}
+
+ssize_t WhoIsMasterRequestPacket::serializePacket(uint8_t* data,
+ uint32_t length) {
+ ssize_t offset = serializeHeader(data, length);
+ if (offset > 0) {
+ uint64_t packed = packDeviceID(senderDeviceID, senderDevicePriority);
+ SERIALIZE_INT64(packed);
+ }
+ return offset;
+}
+
+ssize_t WhoIsMasterRequestPacket::deserializePacket(const uint8_t* data,
+ uint32_t length) {
+ ssize_t offset = deserializeHeader(data, length);
+ if (offset > 0) {
+ uint64_t packed;
+ DESERIALIZE_INT64(packed);
+ senderDeviceID = unpackDeviceID(packed);
+ senderDevicePriority = unpackDevicePriority(packed);
+ }
+ return offset;
+}
+
+ssize_t WhoIsMasterResponsePacket::serializePacket(uint8_t* data,
+ uint32_t length) {
+ ssize_t offset = serializeHeader(data, length);
+ if (offset > 0) {
+ uint64_t packed = packDeviceID(deviceID, devicePriority);
+ SERIALIZE_INT64(packed);
+ }
+ return offset;
+}
+
+ssize_t WhoIsMasterResponsePacket::deserializePacket(const uint8_t* data,
+ uint32_t length) {
+ ssize_t offset = deserializeHeader(data, length);
+ if (offset > 0) {
+ uint64_t packed;
+ DESERIALIZE_INT64(packed);
+ deviceID = unpackDeviceID(packed);
+ devicePriority = unpackDevicePriority(packed);
+ }
+ return offset;
+}
+
+ssize_t SyncRequestPacket::serializePacket(uint8_t* data,
+ uint32_t length) {
+ ssize_t offset = serializeHeader(data, length);
+ if (offset > 0) {
+ SERIALIZE_INT64(clientTxLocalTime);
+ }
+ return offset;
+}
+
+ssize_t SyncRequestPacket::deserializePacket(const uint8_t* data,
+ uint32_t length) {
+ ssize_t offset = deserializeHeader(data, length);
+ if (offset > 0) {
+ DESERIALIZE_INT64(clientTxLocalTime);
+ }
+ return offset;
+}
+
+ssize_t SyncResponsePacket::serializePacket(uint8_t* data,
+ uint32_t length) {
+ ssize_t offset = serializeHeader(data, length);
+ if (offset > 0) {
+ SERIALIZE_INT64(clientTxLocalTime);
+ SERIALIZE_INT64(masterRxCommonTime);
+ SERIALIZE_INT64(masterTxCommonTime);
+ SERIALIZE_INT32(nak);
+ }
+ return offset;
+}
+
+ssize_t SyncResponsePacket::deserializePacket(const uint8_t* data,
+ uint32_t length) {
+ ssize_t offset = deserializeHeader(data, length);
+ if (offset > 0) {
+ DESERIALIZE_INT64(clientTxLocalTime);
+ DESERIALIZE_INT64(masterRxCommonTime);
+ DESERIALIZE_INT64(masterTxCommonTime);
+ DESERIALIZE_INT32(nak);
+ }
+ return offset;
+}
+
+ssize_t MasterAnnouncementPacket::serializePacket(uint8_t* data,
+ uint32_t length) {
+ ssize_t offset = serializeHeader(data, length);
+ if (offset > 0) {
+ uint64_t packed = packDeviceID(deviceID, devicePriority);
+ SERIALIZE_INT64(packed);
+ }
+ return offset;
+}
+
+ssize_t MasterAnnouncementPacket::deserializePacket(const uint8_t* data,
+ uint32_t length) {
+ ssize_t offset = deserializeHeader(data, length);
+ if (offset > 0) {
+ uint64_t packed;
+ DESERIALIZE_INT64(packed);
+ deviceID = unpackDeviceID(packed);
+ devicePriority = unpackDevicePriority(packed);
+ }
+ return offset;
+}
+
+} // namespace android
+
diff --git a/services/common_time/common_time_server_packets.h b/services/common_time/common_time_server_packets.h
new file mode 100644
index 0000000..57ba8a2
--- /dev/null
+++ b/services/common_time/common_time_server_packets.h
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_COMMON_TIME_SERVER_PACKETS_H
+#define ANDROID_COMMON_TIME_SERVER_PACKETS_H
+
+#include <stdint.h>
+#include <common_time/ICommonClock.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,
+};
+
+class TimeServicePacketHeader {
+ public:
+ friend class UniversalTimeServicePacket;
+ // magic number identifying the protocol
+ uint32_t magic;
+
+ // protocol version of the packet
+ uint16_t version;
+
+ // type of the packet
+ TimeServicePacketType packetType;
+
+ // the timeline ID
+ uint64_t timelineID;
+
+ // synchronization group this packet belongs to (used to operate multiple
+ // synchronization domains which all use the same master election endpoint)
+ uint64_t syncGroupID;
+
+ ssize_t serializePacket(uint8_t* data, uint32_t length);
+
+ protected:
+ void initHeader(TimeServicePacketType type,
+ const uint64_t tlID,
+ const uint64_t groupID) {
+ magic = kMagic;
+ version = kCurVersion;
+ packetType = type;
+ timelineID = tlID;
+ syncGroupID = groupID;
+ }
+
+ bool checkPacket(uint64_t expectedSyncGroupID) const {
+ return ((magic == kMagic) &&
+ (version == kCurVersion) &&
+ (!expectedSyncGroupID || (syncGroupID == expectedSyncGroupID)));
+ }
+
+ ssize_t serializeHeader(uint8_t* data, uint32_t length);
+ ssize_t deserializeHeader(const uint8_t* data, uint32_t length);
+
+ private:
+ static const uint32_t kMagic;
+ static const uint16_t kCurVersion;
+};
+
+// packet querying for a suitable master
+class WhoIsMasterRequestPacket : public TimeServicePacketHeader {
+ public:
+ uint64_t senderDeviceID;
+ uint8_t senderDevicePriority;
+
+ void initHeader(const uint64_t groupID) {
+ TimeServicePacketHeader::initHeader(TIME_PACKET_WHO_IS_MASTER_REQUEST,
+ ICommonClock::kInvalidTimelineID,
+ groupID);
+ }
+
+ ssize_t serializePacket(uint8_t* data, uint32_t length);
+ ssize_t deserializePacket(const uint8_t* data, uint32_t length);
+};
+
+// response to a WhoIsMaster request
+class WhoIsMasterResponsePacket : public TimeServicePacketHeader {
+ public:
+ uint64_t deviceID;
+ uint8_t devicePriority;
+
+ void initHeader(const uint64_t tlID, const uint64_t groupID) {
+ TimeServicePacketHeader::initHeader(TIME_PACKET_WHO_IS_MASTER_RESPONSE,
+ tlID, groupID);
+ }
+
+ ssize_t serializePacket(uint8_t* data, uint32_t length);
+ ssize_t deserializePacket(const uint8_t* data, uint32_t length);
+};
+
+// packet sent by a client requesting correspondence between local
+// and common time
+class SyncRequestPacket : public TimeServicePacketHeader {
+ public:
+ // local time when this request was transmitted
+ int64_t clientTxLocalTime;
+
+ void initHeader(const uint64_t tlID, const uint64_t groupID) {
+ TimeServicePacketHeader::initHeader(TIME_PACKET_SYNC_REQUEST,
+ tlID, groupID);
+ }
+
+ ssize_t serializePacket(uint8_t* data, uint32_t length);
+ ssize_t deserializePacket(const uint8_t* data, uint32_t length);
+};
+
+// response to a sync request sent by the master
+class SyncResponsePacket : public TimeServicePacketHeader {
+ public:
+ // 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;
+
+ // flag that is set if the recipient of the sync request is not acting
+ // as a master for the requested timeline
+ uint32_t nak;
+
+ void initHeader(const uint64_t tlID, const uint64_t groupID) {
+ TimeServicePacketHeader::initHeader(TIME_PACKET_SYNC_RESPONSE,
+ tlID, groupID);
+ }
+
+ ssize_t serializePacket(uint8_t* data, uint32_t length);
+ ssize_t deserializePacket(const uint8_t* data, uint32_t length);
+};
+
+// announcement of the master's presence
+class MasterAnnouncementPacket : public TimeServicePacketHeader {
+ public:
+ // the master's device ID
+ uint64_t deviceID;
+ uint8_t devicePriority;
+
+ void initHeader(const uint64_t tlID, const uint64_t groupID) {
+ TimeServicePacketHeader::initHeader(TIME_PACKET_MASTER_ANNOUNCEMENT,
+ tlID, groupID);
+ }
+
+ ssize_t serializePacket(uint8_t* data, uint32_t length);
+ ssize_t deserializePacket(const uint8_t* data, uint32_t length);
+};
+
+class UniversalTimeServicePacket {
+ public:
+ uint16_t packetType;
+ union {
+ WhoIsMasterRequestPacket who_is_master_request;
+ WhoIsMasterResponsePacket who_is_master_response;
+ SyncRequestPacket sync_request;
+ SyncResponsePacket sync_response;
+ MasterAnnouncementPacket master_announcement;
+ } p;
+
+ ssize_t deserializePacket(const uint8_t* data,
+ uint32_t length,
+ uint64_t expectedSyncGroupID);
+};
+
+}; // namespace android
+
+#endif // ANDROID_COMMON_TIME_SERVER_PACKETS_H
+
+
diff --git a/services/common_time/main.cpp b/services/common_time/main.cpp
new file mode 100644
index 0000000..49eb30a
--- /dev/null
+++ b/services/common_time/main.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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 <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+
+#include "common_time_server.h"
+
+int main(int argc, char *argv[]) {
+ using namespace android;
+
+ sp<CommonTimeServer> service = new CommonTimeServer();
+ if (service == NULL)
+ return 1;
+
+ ProcessState::self()->startThreadPool();
+ service->run("CommonTimeServer", ANDROID_PRIORITY_NORMAL);
+
+ IPCThreadState::self()->joinThreadPool();
+ return 0;
+}
+