Merge "Remove CommonClock / CommonTimeManagementService"

am: 72072cd769

Change-Id: I3146a6da8f3fbecc19b344b077e43b5d3e734041
This commit is contained in:
Neil Fuller
2018-06-07 09:52:48 -07:00
committed by android-build-merger
26 changed files with 0 additions and 6565 deletions

View File

@@ -1,405 +0,0 @@
/*
* 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.
*/
package android.os;
import java.net.InetSocketAddress;
import java.util.NoSuchElementException;
import android.os.Binder;
import android.os.CommonTimeUtils;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
/**
* Used for accessing the android common time service's common clock and receiving notifications
* about common time synchronization status changes.
* @hide
*/
public class CommonClock {
/**
* Sentinel value returned by {@link #getTime()} and {@link #getEstimatedError()} when the
* common time service is not able to determine the current common time due to a lack of
* synchronization.
*/
public static final long TIME_NOT_SYNCED = -1;
/**
* Sentinel value returned by {@link #getTimelineId()} when the common time service is not
* currently synced to any timeline.
*/
public static final long INVALID_TIMELINE_ID = 0;
/**
* Sentinel value returned by {@link #getEstimatedError()} when the common time service is not
* currently synced to any timeline.
*/
public static final int ERROR_ESTIMATE_UNKNOWN = 0x7FFFFFFF;
/**
* Value used by {@link #getState()} to indicate that there was an internal error while
* attempting to determine the state of the common time service.
*/
public static final int STATE_INVALID = -1;
/**
* Value used by {@link #getState()} to indicate that the common time service is in its initial
* state and attempting to find the current timeline master, if any. The service will
* transition to either {@link #STATE_CLIENT} if it finds an active master, or to
* {@link #STATE_MASTER} if no active master is found and this client becomes the master of a
* new timeline.
*/
public static final int STATE_INITIAL = 0;
/**
* Value used by {@link #getState()} to indicate that the common time service is in its client
* state and is synchronizing its time to a different timeline master on the network.
*/
public static final int STATE_CLIENT = 1;
/**
* Value used by {@link #getState()} to indicate that the common time service is in its master
* state and is serving as the timeline master for other common time service clients on the
* network.
*/
public static final int STATE_MASTER = 2;
/**
* Value used by {@link #getState()} to indicate that the common time service is in its Ronin
* state. Common time service instances in the client state enter the Ronin state after their
* timeline master becomes unreachable on the network. Common time services who enter the Ronin
* state will begin a new master election for the timeline they were recently clients of. As
* clients detect they are not the winner and drop out of the election, they will transition to
* the {@link #STATE_WAIT_FOR_ELECTION} state. When there is only one client remaining in the
* election, it will assume ownership of the timeline and transition to the
* {@link #STATE_MASTER} state. During the election, all clients will allow their timeline to
* drift without applying correction.
*/
public static final int STATE_RONIN = 3;
/**
* Value used by {@link #getState()} to indicate that the common time service is waiting for a
* master election to conclude and for the new master to announce itself before transitioning to
* the {@link #STATE_CLIENT} state. If no new master announces itself within the timeout
* threshold, the time service will transition back to the {@link #STATE_RONIN} state in order
* to restart the election.
*/
public static final int STATE_WAIT_FOR_ELECTION = 4;
/**
* Name of the underlying native binder service
*/
public static final String SERVICE_NAME = "common_time.clock";
/**
* Class constructor.
* @throws android.os.RemoteException
*/
public CommonClock()
throws RemoteException {
mRemote = ServiceManager.getService(SERVICE_NAME);
if (null == mRemote)
throw new RemoteException();
mInterfaceDesc = mRemote.getInterfaceDescriptor();
mUtils = new CommonTimeUtils(mRemote, mInterfaceDesc);
mRemote.linkToDeath(mDeathHandler, 0);
registerTimelineChangeListener();
}
/**
* Handy class factory method.
*/
static public CommonClock create() {
CommonClock retVal;
try {
retVal = new CommonClock();
}
catch (RemoteException e) {
retVal = null;
}
return retVal;
}
/**
* Release all native resources held by this {@link android.os.CommonClock} instance. Once
* resources have been released, the {@link android.os.CommonClock} instance is disconnected from
* the native service and will throw a {@link android.os.RemoteException} if any of its
* methods are called. Clients should always call release on their client instances before
* releasing their last Java reference to the instance. Failure to do this will cause
* non-deterministic native resource reclamation and may cause the common time service to remain
* active on the network for longer than it should.
*/
public void release() {
unregisterTimelineChangeListener();
if (null != mRemote) {
try {
mRemote.unlinkToDeath(mDeathHandler, 0);
}
catch (NoSuchElementException e) { }
mRemote = null;
}
mUtils = null;
}
/**
* Gets the common clock's current time.
*
* @return a signed 64-bit value representing the current common time in microseconds, or the
* special value {@link #TIME_NOT_SYNCED} if the common time service is currently not
* synchronized.
* @throws android.os.RemoteException
*/
public long getTime()
throws RemoteException {
throwOnDeadServer();
return mUtils.transactGetLong(METHOD_GET_COMMON_TIME, TIME_NOT_SYNCED);
}
/**
* Gets the current estimation of common clock's synchronization accuracy from the common time
* service.
*
* @return a signed 32-bit value representing the common time service's estimation of
* synchronization accuracy in microseconds, or the special value
* {@link #ERROR_ESTIMATE_UNKNOWN} if the common time service is currently not synchronized.
* Negative values indicate that the local server estimates that the nominal common time is
* behind the local server's time (in other words, the local clock is running fast) Positive
* values indicate that the local server estimates that the nominal common time is ahead of the
* local server's time (in other words, the local clock is running slow)
* @throws android.os.RemoteException
*/
public int getEstimatedError()
throws RemoteException {
throwOnDeadServer();
return mUtils.transactGetInt(METHOD_GET_ESTIMATED_ERROR, ERROR_ESTIMATE_UNKNOWN);
}
/**
* Gets the ID of the timeline the common time service is currently synchronizing its clock to.
*
* @return a long representing the unique ID of the timeline the common time service is
* currently synchronizing with, or {@link #INVALID_TIMELINE_ID} if the common time service is
* currently not synchronized.
* @throws android.os.RemoteException
*/
public long getTimelineId()
throws RemoteException {
throwOnDeadServer();
return mUtils.transactGetLong(METHOD_GET_TIMELINE_ID, INVALID_TIMELINE_ID);
}
/**
* Gets the current state of this clock's common time service in the the master election
* algorithm.
*
* @return a integer indicating the current state of the this clock's common time service in the
* master election algorithm or {@link #STATE_INVALID} if there is an internal error.
* @throws android.os.RemoteException
*/
public int getState()
throws RemoteException {
throwOnDeadServer();
return mUtils.transactGetInt(METHOD_GET_STATE, STATE_INVALID);
}
/**
* Gets the IP address and UDP port of the current timeline master.
*
* @return an InetSocketAddress containing the IP address and UDP port of the current timeline
* master, or null if there is no current master.
* @throws android.os.RemoteException
*/
public InetSocketAddress getMasterAddr()
throws RemoteException {
throwOnDeadServer();
return mUtils.transactGetSockaddr(METHOD_GET_MASTER_ADDRESS);
}
/**
* The OnTimelineChangedListener interface defines a method called by the
* {@link android.os.CommonClock} instance to indicate that the time synchronization service has
* either synchronized with a new timeline, or is no longer a member of any timeline. The
* client application can implement this interface and register the listener with the
* {@link #setTimelineChangedListener(OnTimelineChangedListener)} method.
*/
public interface OnTimelineChangedListener {
/**
* Method called when the time service's timeline has changed.
*
* @param newTimelineId a long which uniquely identifies the timeline the time
* synchronization service is now a member of, or {@link #INVALID_TIMELINE_ID} if the the
* service is not synchronized to any timeline.
*/
void onTimelineChanged(long newTimelineId);
}
/**
* Registers an OnTimelineChangedListener interface.
* <p>Call this method with a null listener to stop receiving server death notifications.
*/
public void setTimelineChangedListener(OnTimelineChangedListener listener) {
synchronized (mListenerLock) {
mTimelineChangedListener = listener;
}
}
/**
* The OnServerDiedListener interface defines a method called by the
* {@link android.os.CommonClock} instance to indicate that the connection to the native media
* server has been broken and that the {@link android.os.CommonClock} instance will need to be
* released and re-created. The client application can implement this interface and register
* the listener with the {@link #setServerDiedListener(OnServerDiedListener)} method.
*/
public interface OnServerDiedListener {
/**
* Method called when the native media server has died. <p>If the native common time
* service encounters a fatal error and needs to restart, the binder connection from the
* {@link android.os.CommonClock} instance to the common time service will be broken. To
* restore functionality, clients should {@link #release()} their old visualizer and create
* a new instance.
*/
void onServerDied();
}
/**
* Registers an OnServerDiedListener interface.
* <p>Call this method with a null listener to stop receiving server death notifications.
*/
public void setServerDiedListener(OnServerDiedListener listener) {
synchronized (mListenerLock) {
mServerDiedListener = listener;
}
}
protected void finalize() throws Throwable { release(); }
private void throwOnDeadServer() throws RemoteException {
if ((null == mRemote) || (null == mUtils))
throw new RemoteException();
}
private final Object mListenerLock = new Object();
private OnTimelineChangedListener mTimelineChangedListener = null;
private OnServerDiedListener mServerDiedListener = null;
private IBinder mRemote = null;
private String mInterfaceDesc = "";
private CommonTimeUtils mUtils;
private IBinder.DeathRecipient mDeathHandler = new IBinder.DeathRecipient() {
public void binderDied() {
synchronized (mListenerLock) {
if (null != mServerDiedListener)
mServerDiedListener.onServerDied();
}
}
};
private class TimelineChangedListener extends Binder {
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
switch (code) {
case METHOD_CBK_ON_TIMELINE_CHANGED:
data.enforceInterface(DESCRIPTOR);
long timelineId = data.readLong();
synchronized (mListenerLock) {
if (null != mTimelineChangedListener)
mTimelineChangedListener.onTimelineChanged(timelineId);
}
return true;
}
return super.onTransact(code, data, reply, flags);
}
private static final String DESCRIPTOR = "android.os.ICommonClockListener";
};
private TimelineChangedListener mCallbackTgt = null;
private void registerTimelineChangeListener() throws RemoteException {
if (null != mCallbackTgt)
return;
boolean success = false;
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
mCallbackTgt = new TimelineChangedListener();
try {
data.writeInterfaceToken(mInterfaceDesc);
data.writeStrongBinder(mCallbackTgt);
mRemote.transact(METHOD_REGISTER_LISTENER, data, reply, 0);
success = (0 == reply.readInt());
}
catch (RemoteException e) {
success = false;
}
finally {
reply.recycle();
data.recycle();
}
// Did we catch a remote exception or fail to register our callback target? If so, our
// object must already be dead (or be as good as dead). Clear out all of our state so that
// our other methods will properly indicate a dead object.
if (!success) {
mCallbackTgt = null;
mRemote = null;
mUtils = null;
}
}
private void unregisterTimelineChangeListener() {
if (null == mCallbackTgt)
return;
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
try {
data.writeInterfaceToken(mInterfaceDesc);
data.writeStrongBinder(mCallbackTgt);
mRemote.transact(METHOD_UNREGISTER_LISTENER, data, reply, 0);
}
catch (RemoteException e) { }
finally {
reply.recycle();
data.recycle();
mCallbackTgt = null;
}
}
private static final int METHOD_IS_COMMON_TIME_VALID = IBinder.FIRST_CALL_TRANSACTION;
private static final int METHOD_COMMON_TIME_TO_LOCAL_TIME = METHOD_IS_COMMON_TIME_VALID + 1;
private static final int METHOD_LOCAL_TIME_TO_COMMON_TIME = METHOD_COMMON_TIME_TO_LOCAL_TIME + 1;
private static final int METHOD_GET_COMMON_TIME = METHOD_LOCAL_TIME_TO_COMMON_TIME + 1;
private static final int METHOD_GET_COMMON_FREQ = METHOD_GET_COMMON_TIME + 1;
private static final int METHOD_GET_LOCAL_TIME = METHOD_GET_COMMON_FREQ + 1;
private static final int METHOD_GET_LOCAL_FREQ = METHOD_GET_LOCAL_TIME + 1;
private static final int METHOD_GET_ESTIMATED_ERROR = METHOD_GET_LOCAL_FREQ + 1;
private static final int METHOD_GET_TIMELINE_ID = METHOD_GET_ESTIMATED_ERROR + 1;
private static final int METHOD_GET_STATE = METHOD_GET_TIMELINE_ID + 1;
private static final int METHOD_GET_MASTER_ADDRESS = METHOD_GET_STATE + 1;
private static final int METHOD_REGISTER_LISTENER = METHOD_GET_MASTER_ADDRESS + 1;
private static final int METHOD_UNREGISTER_LISTENER = METHOD_REGISTER_LISTENER + 1;
private static final int METHOD_CBK_ON_TIMELINE_CHANGED = IBinder.FIRST_CALL_TRANSACTION;
}

View File

@@ -1,447 +0,0 @@
/*
* 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.
*/
package android.os;
import java.net.InetSocketAddress;
import java.util.NoSuchElementException;
import android.os.CommonTimeUtils;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
/**
* Used for configuring and controlling the status of the android common time service.
* @hide
*/
public class CommonTimeConfig {
/**
* Successful operation.
*/
public static final int SUCCESS = 0;
/**
* Unspecified error.
*/
public static final int ERROR = -1;
/**
* Operation failed due to bad parameter value.
*/
public static final int ERROR_BAD_VALUE = -4;
/**
* Operation failed due to dead remote object.
*/
public static final int ERROR_DEAD_OBJECT = -7;
/**
* Sentinel value returned by {@link #getMasterElectionGroupId()} when an error occurs trying to
* fetch the master election group.
*/
public static final long INVALID_GROUP_ID = -1;
/**
* Name of the underlying native binder service
*/
public static final String SERVICE_NAME = "common_time.config";
/**
* Class constructor.
* @throws android.os.RemoteException
*/
public CommonTimeConfig()
throws RemoteException {
mRemote = ServiceManager.getService(SERVICE_NAME);
if (null == mRemote)
throw new RemoteException();
mInterfaceDesc = mRemote.getInterfaceDescriptor();
mUtils = new CommonTimeUtils(mRemote, mInterfaceDesc);
mRemote.linkToDeath(mDeathHandler, 0);
}
/**
* Handy class factory method.
*/
static public CommonTimeConfig create() {
CommonTimeConfig retVal;
try {
retVal = new CommonTimeConfig();
}
catch (RemoteException e) {
retVal = null;
}
return retVal;
}
/**
* Release all native resources held by this {@link android.os.CommonTimeConfig} instance. Once
* resources have been released, the {@link android.os.CommonTimeConfig} instance is
* disconnected from the native service and will throw a {@link android.os.RemoteException} if
* any of its methods are called. Clients should always call release on their client instances
* before releasing their last Java reference to the instance. Failure to do this will cause
* non-deterministic native resource reclamation and may cause the common time service to remain
* active on the network for longer than it should.
*/
public void release() {
if (null != mRemote) {
try {
mRemote.unlinkToDeath(mDeathHandler, 0);
}
catch (NoSuchElementException e) { }
mRemote = null;
}
mUtils = null;
}
/**
* Gets the current priority of the common time service used in the master election protocol.
*
* @return an 8 bit value indicating the priority of this common time service relative to other
* common time services operating in the same domain.
* @throws android.os.RemoteException
*/
public byte getMasterElectionPriority()
throws RemoteException {
throwOnDeadServer();
return (byte)mUtils.transactGetInt(METHOD_GET_MASTER_ELECTION_PRIORITY, -1);
}
/**
* Sets the current priority of the common time service used in the master election protocol.
*
* @param priority priority of the common time service used in the master election protocol.
* Lower numbers are lower priority.
* @return {@link #SUCCESS} in case of success,
* {@link #ERROR} or {@link #ERROR_DEAD_OBJECT} in case of failure.
*/
public int setMasterElectionPriority(byte priority) {
if (checkDeadServer())
return ERROR_DEAD_OBJECT;
return mUtils.transactSetInt(METHOD_SET_MASTER_ELECTION_PRIORITY, priority);
}
/**
* Gets the IP endpoint used by the time service to participate in the master election protocol.
*
* @return an InetSocketAddress containing the IP address and UDP port being used by the
* system's common time service to participate in the master election protocol.
* @throws android.os.RemoteException
*/
public InetSocketAddress getMasterElectionEndpoint()
throws RemoteException {
throwOnDeadServer();
return mUtils.transactGetSockaddr(METHOD_GET_MASTER_ELECTION_ENDPOINT);
}
/**
* Sets the IP endpoint used by the common time service to participate in the master election
* protocol.
*
* @param ep The IP address and UDP port to be used by the common time service to participate in
* the master election protocol. The supplied IP address must be either the broadcast or
* multicast address, unicast addresses are considered to be illegal values.
* @return {@link #SUCCESS} in case of success,
* {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure.
*/
public int setMasterElectionEndpoint(InetSocketAddress ep) {
if (checkDeadServer())
return ERROR_DEAD_OBJECT;
return mUtils.transactSetSockaddr(METHOD_SET_MASTER_ELECTION_ENDPOINT, ep);
}
/**
* Gets the current group ID used by the common time service in the master election protocol.
*
* @return The 64-bit group ID of the common time service.
* @throws android.os.RemoteException
*/
public long getMasterElectionGroupId()
throws RemoteException {
throwOnDeadServer();
return mUtils.transactGetLong(METHOD_GET_MASTER_ELECTION_GROUP_ID, INVALID_GROUP_ID);
}
/**
* Sets the current group ID used by the common time service in the master election protocol.
*
* @param id The 64-bit group ID of the common time service.
* @return {@link #SUCCESS} in case of success,
* {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure.
*/
public int setMasterElectionGroupId(long id) {
if (checkDeadServer())
return ERROR_DEAD_OBJECT;
return mUtils.transactSetLong(METHOD_SET_MASTER_ELECTION_GROUP_ID, id);
}
/**
* Gets the name of the network interface which the common time service attempts to bind to.
*
* @return a string with the network interface name which the common time service is bound to,
* or null if the service is currently unbound. Examples of interface names are things like
* "eth0", or "wlan0".
* @throws android.os.RemoteException
*/
public String getInterfaceBinding()
throws RemoteException {
throwOnDeadServer();
String ifaceName = mUtils.transactGetString(METHOD_GET_INTERFACE_BINDING, null);
if ((null != ifaceName) && (0 == ifaceName.length()))
return null;
return ifaceName;
}
/**
* Sets the name of the network interface which the common time service should attempt to bind
* to.
*
* @param ifaceName The name of the network interface ("eth0", "wlan0", etc...) wich the common
* time service should attempt to bind to, or null to force the common time service to unbind
* from the network and run in networkless mode.
* @return {@link #SUCCESS} in case of success,
* {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure.
*/
public int setNetworkBinding(String ifaceName) {
if (checkDeadServer())
return ERROR_DEAD_OBJECT;
return mUtils.transactSetString(METHOD_SET_INTERFACE_BINDING,
(null == ifaceName) ? "" : ifaceName);
}
/**
* Gets the amount of time the common time service will wait between master announcements when
* it is the timeline master.
*
* @return The time (in milliseconds) between master announcements.
* @throws android.os.RemoteException
*/
public int getMasterAnnounceInterval()
throws RemoteException {
throwOnDeadServer();
return mUtils.transactGetInt(METHOD_GET_MASTER_ANNOUNCE_INTERVAL, -1);
}
/**
* Sets the amount of time the common time service will wait between master announcements when
* it is the timeline master.
*
* @param interval The time (in milliseconds) between master announcements.
* @return {@link #SUCCESS} in case of success,
* {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure.
*/
public int setMasterAnnounceInterval(int interval) {
if (checkDeadServer())
return ERROR_DEAD_OBJECT;
return mUtils.transactSetInt(METHOD_SET_MASTER_ANNOUNCE_INTERVAL, interval);
}
/**
* Gets the amount of time the common time service will wait between time synchronization
* requests when it is the client of another common time service on the network.
*
* @return The time (in milliseconds) between time sync requests.
* @throws android.os.RemoteException
*/
public int getClientSyncInterval()
throws RemoteException {
throwOnDeadServer();
return mUtils.transactGetInt(METHOD_GET_CLIENT_SYNC_INTERVAL, -1);
}
/**
* Sets the amount of time the common time service will wait between time synchronization
* requests when it is the client of another common time service on the network.
*
* @param interval The time (in milliseconds) between time sync requests.
* @return {@link #SUCCESS} in case of success,
* {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure.
*/
public int setClientSyncInterval(int interval) {
if (checkDeadServer())
return ERROR_DEAD_OBJECT;
return mUtils.transactSetInt(METHOD_SET_CLIENT_SYNC_INTERVAL, interval);
}
/**
* Gets the panic threshold for the estimated error level of the common time service. When the
* common time service's estimated error rises above this level, the service will panic and
* reset, causing a discontinuity in the currently synchronized timeline.
*
* @return The threshold (in microseconds) past which the common time service will panic.
* @throws android.os.RemoteException
*/
public int getPanicThreshold()
throws RemoteException {
throwOnDeadServer();
return mUtils.transactGetInt(METHOD_GET_PANIC_THRESHOLD, -1);
}
/**
* Sets the panic threshold for the estimated error level of the common time service. When the
* common time service's estimated error rises above this level, the service will panic and
* reset, causing a discontinuity in the currently synchronized timeline.
*
* @param threshold The threshold (in microseconds) past which the common time service will
* panic.
* @return {@link #SUCCESS} in case of success,
* {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure.
*/
public int setPanicThreshold(int threshold) {
if (checkDeadServer())
return ERROR_DEAD_OBJECT;
return mUtils.transactSetInt(METHOD_SET_PANIC_THRESHOLD, threshold);
}
/**
* Gets the current state of the common time service's auto disable flag.
*
* @return The current state of the common time service's auto disable flag.
* @throws android.os.RemoteException
*/
public boolean getAutoDisable()
throws RemoteException {
throwOnDeadServer();
return (1 == mUtils.transactGetInt(METHOD_GET_AUTO_DISABLE, 1));
}
/**
* Sets the current state of the common time service's auto disable flag. When the time
* service's auto disable flag is set, it will automatically cease all network activity when
* it has no active local clients, resuming activity the next time the service has interested
* local clients. When the auto disabled flag is cleared, the common time service will continue
* to participate the time synchronization group even when it has no active local clients.
*
* @param autoDisable The desired state of the common time service's auto disable flag.
* @return {@link #SUCCESS} in case of success,
* {@link #ERROR} or {@link #ERROR_DEAD_OBJECT} in case of failure.
*/
public int setAutoDisable(boolean autoDisable) {
if (checkDeadServer())
return ERROR_DEAD_OBJECT;
return mUtils.transactSetInt(METHOD_SET_AUTO_DISABLE, autoDisable ? 1 : 0);
}
/**
* At startup, the time service enters the initial state and remains there until it is given a
* network interface to bind to. Common time will be unavailable to clients of the common time
* service until the service joins a network (even an empty network). Devices may use the
* {@link #forceNetworklessMasterMode()} method to force a time service in the INITIAL state
* with no network configuration to assume MASTER status for a brand new timeline in order to
* allow clients of the common time service to operate, even though the device is isolated and
* not on any network. When a networkless master does join a network, it will defer to any
* masters already on the network, or continue to maintain the timeline it made up during its
* networkless state if no other masters are detected. Attempting to force a client into master
* mode while it is actively bound to a network will fail with the status code {@link #ERROR}
*
* @return {@link #SUCCESS} in case of success,
* {@link #ERROR} or {@link #ERROR_DEAD_OBJECT} in case of failure.
*/
public int forceNetworklessMasterMode() {
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
try {
data.writeInterfaceToken(mInterfaceDesc);
mRemote.transact(METHOD_FORCE_NETWORKLESS_MASTER_MODE, data, reply, 0);
return reply.readInt();
}
catch (RemoteException e) {
return ERROR_DEAD_OBJECT;
}
finally {
reply.recycle();
data.recycle();
}
}
/**
* The OnServerDiedListener interface defines a method called by the
* {@link android.os.CommonTimeConfig} instance to indicate that the connection to the native
* media server has been broken and that the {@link android.os.CommonTimeConfig} instance will
* need to be released and re-created. The client application can implement this interface and
* register the listener with the {@link #setServerDiedListener(OnServerDiedListener)} method.
*/
public interface OnServerDiedListener {
/**
* Method called when the native common time service has died. <p>If the native common time
* service encounters a fatal error and needs to restart, the binder connection from the
* {@link android.os.CommonTimeConfig} instance to the common time service will be broken.
*/
void onServerDied();
}
/**
* Registers an OnServerDiedListener interface.
* <p>Call this method with a null listener to stop receiving server death notifications.
*/
public void setServerDiedListener(OnServerDiedListener listener) {
synchronized (mListenerLock) {
mServerDiedListener = listener;
}
}
protected void finalize() throws Throwable { release(); }
private boolean checkDeadServer() {
return ((null == mRemote) || (null == mUtils));
}
private void throwOnDeadServer() throws RemoteException {
if (checkDeadServer())
throw new RemoteException();
}
private final Object mListenerLock = new Object();
private OnServerDiedListener mServerDiedListener = null;
private IBinder mRemote = null;
private String mInterfaceDesc = "";
private CommonTimeUtils mUtils;
private IBinder.DeathRecipient mDeathHandler = new IBinder.DeathRecipient() {
public void binderDied() {
synchronized (mListenerLock) {
if (null != mServerDiedListener)
mServerDiedListener.onServerDied();
}
}
};
private static final int METHOD_GET_MASTER_ELECTION_PRIORITY = IBinder.FIRST_CALL_TRANSACTION;
private static final int METHOD_SET_MASTER_ELECTION_PRIORITY = METHOD_GET_MASTER_ELECTION_PRIORITY + 1;
private static final int METHOD_GET_MASTER_ELECTION_ENDPOINT = METHOD_SET_MASTER_ELECTION_PRIORITY + 1;
private static final int METHOD_SET_MASTER_ELECTION_ENDPOINT = METHOD_GET_MASTER_ELECTION_ENDPOINT + 1;
private static final int METHOD_GET_MASTER_ELECTION_GROUP_ID = METHOD_SET_MASTER_ELECTION_ENDPOINT + 1;
private static final int METHOD_SET_MASTER_ELECTION_GROUP_ID = METHOD_GET_MASTER_ELECTION_GROUP_ID + 1;
private static final int METHOD_GET_INTERFACE_BINDING = METHOD_SET_MASTER_ELECTION_GROUP_ID + 1;
private static final int METHOD_SET_INTERFACE_BINDING = METHOD_GET_INTERFACE_BINDING + 1;
private static final int METHOD_GET_MASTER_ANNOUNCE_INTERVAL = METHOD_SET_INTERFACE_BINDING + 1;
private static final int METHOD_SET_MASTER_ANNOUNCE_INTERVAL = METHOD_GET_MASTER_ANNOUNCE_INTERVAL + 1;
private static final int METHOD_GET_CLIENT_SYNC_INTERVAL = METHOD_SET_MASTER_ANNOUNCE_INTERVAL + 1;
private static final int METHOD_SET_CLIENT_SYNC_INTERVAL = METHOD_GET_CLIENT_SYNC_INTERVAL + 1;
private static final int METHOD_GET_PANIC_THRESHOLD = METHOD_SET_CLIENT_SYNC_INTERVAL + 1;
private static final int METHOD_SET_PANIC_THRESHOLD = METHOD_GET_PANIC_THRESHOLD + 1;
private static final int METHOD_GET_AUTO_DISABLE = METHOD_SET_PANIC_THRESHOLD + 1;
private static final int METHOD_SET_AUTO_DISABLE = METHOD_GET_AUTO_DISABLE + 1;
private static final int METHOD_FORCE_NETWORKLESS_MASTER_MODE = METHOD_SET_AUTO_DISABLE + 1;
}

View File

@@ -1,293 +0,0 @@
/*
* 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.
*/
package android.os;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.util.Locale;
import static android.system.OsConstants.*;
class CommonTimeUtils {
/**
* Successful operation.
*/
public static final int SUCCESS = 0;
/**
* Unspecified error.
*/
public static final int ERROR = -1;
/**
* Operation failed due to bad parameter value.
*/
public static final int ERROR_BAD_VALUE = -4;
/**
* Operation failed due to dead remote object.
*/
public static final int ERROR_DEAD_OBJECT = -7;
public CommonTimeUtils(IBinder remote, String interfaceDesc) {
mRemote = remote;
mInterfaceDesc = interfaceDesc;
}
public int transactGetInt(int method_code, int error_ret_val)
throws RemoteException {
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
int ret_val;
try {
int res;
data.writeInterfaceToken(mInterfaceDesc);
mRemote.transact(method_code, data, reply, 0);
res = reply.readInt();
ret_val = (0 == res) ? reply.readInt() : error_ret_val;
}
finally {
reply.recycle();
data.recycle();
}
return ret_val;
}
public int transactSetInt(int method_code, int val) {
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
try {
data.writeInterfaceToken(mInterfaceDesc);
data.writeInt(val);
mRemote.transact(method_code, data, reply, 0);
return reply.readInt();
}
catch (RemoteException e) {
return ERROR_DEAD_OBJECT;
}
finally {
reply.recycle();
data.recycle();
}
}
public long transactGetLong(int method_code, long error_ret_val)
throws RemoteException {
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
long ret_val;
try {
int res;
data.writeInterfaceToken(mInterfaceDesc);
mRemote.transact(method_code, data, reply, 0);
res = reply.readInt();
ret_val = (0 == res) ? reply.readLong() : error_ret_val;
}
finally {
reply.recycle();
data.recycle();
}
return ret_val;
}
public int transactSetLong(int method_code, long val) {
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
try {
data.writeInterfaceToken(mInterfaceDesc);
data.writeLong(val);
mRemote.transact(method_code, data, reply, 0);
return reply.readInt();
}
catch (RemoteException e) {
return ERROR_DEAD_OBJECT;
}
finally {
reply.recycle();
data.recycle();
}
}
public String transactGetString(int method_code, String error_ret_val)
throws RemoteException {
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
String ret_val;
try {
int res;
data.writeInterfaceToken(mInterfaceDesc);
mRemote.transact(method_code, data, reply, 0);
res = reply.readInt();
ret_val = (0 == res) ? reply.readString() : error_ret_val;
}
finally {
reply.recycle();
data.recycle();
}
return ret_val;
}
public int transactSetString(int method_code, String val) {
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
try {
data.writeInterfaceToken(mInterfaceDesc);
data.writeString(val);
mRemote.transact(method_code, data, reply, 0);
return reply.readInt();
}
catch (RemoteException e) {
return ERROR_DEAD_OBJECT;
}
finally {
reply.recycle();
data.recycle();
}
}
public InetSocketAddress transactGetSockaddr(int method_code)
throws RemoteException {
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
InetSocketAddress ret_val = null;
try {
int res;
data.writeInterfaceToken(mInterfaceDesc);
mRemote.transact(method_code, data, reply, 0);
res = reply.readInt();
if (0 == res) {
int type;
int port = 0;
String addrStr = null;
type = reply.readInt();
if (AF_INET == type) {
int addr = reply.readInt();
port = reply.readInt();
addrStr = String.format(Locale.US, "%d.%d.%d.%d",
(addr >> 24) & 0xFF,
(addr >> 16) & 0xFF,
(addr >> 8) & 0xFF,
addr & 0xFF);
} else if (AF_INET6 == type) {
int addr1 = reply.readInt();
int addr2 = reply.readInt();
int addr3 = reply.readInt();
int addr4 = reply.readInt();
port = reply.readInt();
int flowinfo = reply.readInt();
int scope_id = reply.readInt();
addrStr = String.format(Locale.US, "[%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X]",
(addr1 >> 16) & 0xFFFF, addr1 & 0xFFFF,
(addr2 >> 16) & 0xFFFF, addr2 & 0xFFFF,
(addr3 >> 16) & 0xFFFF, addr3 & 0xFFFF,
(addr4 >> 16) & 0xFFFF, addr4 & 0xFFFF);
}
if (null != addrStr) {
ret_val = new InetSocketAddress(addrStr, port);
}
}
}
finally {
reply.recycle();
data.recycle();
}
return ret_val;
}
public int transactSetSockaddr(int method_code, InetSocketAddress addr) {
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
int ret_val = ERROR;
try {
data.writeInterfaceToken(mInterfaceDesc);
if (null == addr) {
data.writeInt(0);
} else {
data.writeInt(1);
final InetAddress a = addr.getAddress();
final byte[] b = a.getAddress();
final int p = addr.getPort();
if (a instanceof Inet4Address) {
int v4addr = (((int)b[0] & 0xFF) << 24) |
(((int)b[1] & 0xFF) << 16) |
(((int)b[2] & 0xFF) << 8) |
((int)b[3] & 0xFF);
data.writeInt(AF_INET);
data.writeInt(v4addr);
data.writeInt(p);
} else
if (a instanceof Inet6Address) {
int i;
Inet6Address v6 = (Inet6Address)a;
data.writeInt(AF_INET6);
for (i = 0; i < 4; ++i) {
int aword = (((int)b[(i*4) + 0] & 0xFF) << 24) |
(((int)b[(i*4) + 1] & 0xFF) << 16) |
(((int)b[(i*4) + 2] & 0xFF) << 8) |
((int)b[(i*4) + 3] & 0xFF);
data.writeInt(aword);
}
data.writeInt(p);
data.writeInt(0); // flow info
data.writeInt(v6.getScopeId());
} else {
return ERROR_BAD_VALUE;
}
}
mRemote.transact(method_code, data, reply, 0);
ret_val = reply.readInt();
}
catch (RemoteException e) {
ret_val = ERROR_DEAD_OBJECT;
}
finally {
reply.recycle();
data.recycle();
}
return ret_val;
}
private IBinder mRemote;
private String mInterfaceDesc;
};

View File

@@ -1,39 +0,0 @@
LOCAL_PATH:= $(call my-dir)
#
# common_time_service
#
include $(CLEAR_VARS)
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 \
main.cpp \
utils.cpp \
LinearTransform.cpp
# Uncomment to enable vesbose logging and debug service.
#TIME_SERVICE_DEBUG=true
ifeq ($(TIME_SERVICE_DEBUG), true)
LOCAL_SRC_FILES += diag_thread.cpp
LOCAL_CFLAGS += -DTIME_SERVICE_DEBUG
endif
LOCAL_SHARED_LIBRARIES := \
libbinder \
libcommon_time_client \
libutils \
liblog
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := common_time
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
include $(BUILD_EXECUTABLE)

View File

@@ -1,281 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define __STDC_LIMIT_MACROS
#include "LinearTransform.h"
#include <assert.h>
// disable sanitize as these functions may intentionally overflow (see comments below).
// the ifdef can be removed when host builds use clang.
#if defined(__clang__)
#define ATTRIBUTE_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer")))
#else
#define ATTRIBUTE_NO_SANITIZE_INTEGER
#endif
namespace android {
// sanitize failure with T = int32_t and x = 0x80000000
template<class T>
ATTRIBUTE_NO_SANITIZE_INTEGER
static inline T ABS(T x) { return (x < 0) ? -x : x; }
// Static math methods involving linear transformations
// remote sanitize failure on overflow case.
ATTRIBUTE_NO_SANITIZE_INTEGER
static bool scale_u64_to_u64(
uint64_t val,
uint32_t N,
uint32_t D,
uint64_t* res,
bool round_up_not_down) {
uint64_t tmp1, tmp2;
uint32_t r;
assert(res);
assert(D);
// Let U32(X) denote a uint32_t containing the upper 32 bits of a 64 bit
// integer X.
// Let L32(X) denote a uint32_t containing the lower 32 bits of a 64 bit
// integer X.
// Let X[A, B] with A <= B denote bits A through B of the integer X.
// Let (A | B) denote the concatination of two 32 bit ints, A and B.
// IOW X = (A | B) => U32(X) == A && L32(X) == B
//
// compute M = val * N (a 96 bit int)
// ---------------------------------
// tmp2 = U32(val) * N (a 64 bit int)
// tmp1 = L32(val) * N (a 64 bit int)
// which means
// M = val * N = (tmp2 << 32) + tmp1
tmp2 = (val >> 32) * N;
tmp1 = (val & UINT32_MAX) * N;
// compute M[32, 95]
// tmp2 = tmp2 + U32(tmp1)
// = (U32(val) * N) + U32(L32(val) * N)
// = M[32, 95]
tmp2 += tmp1 >> 32;
// if M[64, 95] >= D, then M/D has bits > 63 set and we have
// an overflow.
if ((tmp2 >> 32) >= D) {
*res = UINT64_MAX;
return false;
}
// Divide. Going in we know
// tmp2 = M[32, 95]
// U32(tmp2) < D
r = tmp2 % D;
tmp2 /= D;
// At this point
// tmp1 = L32(val) * N
// tmp2 = M[32, 95] / D
// = (M / D)[32, 95]
// r = M[32, 95] % D
// U32(tmp2) = 0
//
// compute tmp1 = (r | M[0, 31])
tmp1 = (tmp1 & UINT32_MAX) | ((uint64_t)r << 32);
// Divide again. Keep the remainder around in order to round properly.
r = tmp1 % D;
tmp1 /= D;
// At this point
// tmp2 = (M / D)[32, 95]
// tmp1 = (M / D)[ 0, 31]
// r = M % D
// U32(tmp1) = 0
// U32(tmp2) = 0
// Pack the result and deal with the round-up case (As well as the
// remote possiblility over overflow in such a case).
*res = (tmp2 << 32) | tmp1;
if (r && round_up_not_down) {
++(*res);
if (!(*res)) {
*res = UINT64_MAX;
return false;
}
}
return true;
}
// at least one known sanitize failure (see comment below)
ATTRIBUTE_NO_SANITIZE_INTEGER
static bool linear_transform_s64_to_s64(
int64_t val,
int64_t basis1,
int32_t N,
uint32_t D,
bool invert_frac,
int64_t basis2,
int64_t* out) {
uint64_t scaled, res;
uint64_t abs_val;
bool is_neg;
if (!out)
return false;
// Compute abs(val - basis_64). Keep track of whether or not this delta
// will be negative after the scale opertaion.
if (val < basis1) {
is_neg = true;
abs_val = basis1 - val;
} else {
is_neg = false;
abs_val = val - basis1;
}
if (N < 0)
is_neg = !is_neg;
if (!scale_u64_to_u64(abs_val,
invert_frac ? D : ABS(N),
invert_frac ? ABS(N) : D,
&scaled,
is_neg))
return false; // overflow/undeflow
// if scaled is >= 0x8000<etc>, then we are going to overflow or
// underflow unless ABS(basis2) is large enough to pull us back into the
// non-overflow/underflow region.
if (scaled & INT64_MIN) {
if (is_neg && (basis2 < 0))
return false; // certain underflow
if (!is_neg && (basis2 >= 0))
return false; // certain overflow
if (ABS(basis2) <= static_cast<int64_t>(scaled & INT64_MAX))
return false; // not enough
// Looks like we are OK
*out = (is_neg ? (-scaled) : scaled) + basis2;
} else {
// Scaled fits within signed bounds, so we just need to check for
// over/underflow for two signed integers. Basically, if both scaled
// and basis2 have the same sign bit, and the result has a different
// sign bit, then we have under/overflow. An easy way to compute this
// is
// (scaled_signbit XNOR basis_signbit) &&
// (scaled_signbit XOR res_signbit)
// ==
// (scaled_signbit XOR basis_signbit XOR 1) &&
// (scaled_signbit XOR res_signbit)
if (is_neg)
scaled = -scaled; // known sanitize failure
res = scaled + basis2;
if ((scaled ^ basis2 ^ INT64_MIN) & (scaled ^ res) & INT64_MIN)
return false;
*out = res;
}
return true;
}
bool LinearTransform::doForwardTransform(int64_t a_in, int64_t* b_out) const {
if (0 == a_to_b_denom)
return false;
return linear_transform_s64_to_s64(a_in,
a_zero,
a_to_b_numer,
a_to_b_denom,
false,
b_zero,
b_out);
}
bool LinearTransform::doReverseTransform(int64_t b_in, int64_t* a_out) const {
if (0 == a_to_b_numer)
return false;
return linear_transform_s64_to_s64(b_in,
b_zero,
a_to_b_numer,
a_to_b_denom,
true,
a_zero,
a_out);
}
template <class T> void LinearTransform::reduce(T* N, T* D) {
T a, b;
if (!N || !D || !(*D)) {
assert(false);
return;
}
a = *N;
b = *D;
if (a == 0) {
*D = 1;
return;
}
// This implements Euclid's method to find GCD.
if (a < b) {
T tmp = a;
a = b;
b = tmp;
}
while (1) {
// a is now the greater of the two.
const T remainder = a % b;
if (remainder == 0) {
*N /= b;
*D /= b;
return;
}
// by swapping remainder and b, we are guaranteeing that a is
// still the greater of the two upon entrance to the loop.
a = b;
b = remainder;
}
};
template void LinearTransform::reduce<uint64_t>(uint64_t* N, uint64_t* D);
template void LinearTransform::reduce<uint32_t>(uint32_t* N, uint32_t* D);
// sanitize failure if *N = 0x80000000
ATTRIBUTE_NO_SANITIZE_INTEGER
void LinearTransform::reduce(int32_t* N, uint32_t* D) {
if (N && D && *D) {
if (*N < 0) {
*N = -(*N);
reduce(reinterpret_cast<uint32_t*>(N), D);
*N = -(*N);
} else {
reduce(reinterpret_cast<uint32_t*>(N), D);
}
}
}
} // namespace android

View File

@@ -1,64 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _LINEAR_TRANSFORM_H
#define _LINEAR_TRANSFORM_H
#include <stdint.h>
namespace android {
// LinearTransform defines a structure which hold the definition of a
// transformation from single dimensional coordinate system A into coordinate
// system B (and back again). Values in A and in B are 64 bit, the linear
// scale factor is expressed as a rational number using two 32 bit values.
//
// Specifically, let
// f(a) = b
// F(b) = f^-1(b) = a
// then
//
// f(a) = (((a - a_zero) * a_to_b_numer) / a_to_b_denom) + b_zero;
//
// and
//
// F(b) = (((b - b_zero) * a_to_b_denom) / a_to_b_numer) + a_zero;
//
struct LinearTransform {
int64_t a_zero;
int64_t b_zero;
int32_t a_to_b_numer;
uint32_t a_to_b_denom;
// Transform from A->B
// Returns true on success, or false in the case of a singularity or an
// overflow.
bool doForwardTransform(int64_t a_in, int64_t* b_out) const;
// Transform from B->A
// Returns true on success, or false in the case of a singularity or an
// overflow.
bool doReverseTransform(int64_t b_in, int64_t* a_out) const;
// Helpers which will reduce the fraction N/D using Euclid's method.
template <class T> static void reduce(T* N, T* D);
static void reduce(int32_t* N, uint32_t* D);
};
}
#endif // _LINEAR_TRANSFORM_H

View File

@@ -1,423 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* A service that exchanges time synchronization information between
* a master that defines a timeline and clients that follow the timeline.
*/
#define __STDC_LIMIT_MACROS
#define LOG_TAG "common_time"
#include <utils/Log.h>
#include <inttypes.h>
#include <stdint.h>
#include <common_time/local_clock.h>
#include <assert.h>
#include "clock_recovery.h"
#include "common_clock.h"
#ifdef TIME_SERVICE_DEBUG
#include "diag_thread.h"
#endif
// Define log macro so we can make LOGV into LOGE when we are exclusively
// debugging this code.
#ifdef TIME_SERVICE_DEBUG
#define LOG_TS ALOGE
#else
#define LOG_TS ALOGV
#endif
namespace android {
ClockRecoveryLoop::ClockRecoveryLoop(LocalClock* local_clock,
CommonClock* common_clock) {
assert(NULL != local_clock);
assert(NULL != common_clock);
local_clock_ = local_clock;
common_clock_ = common_clock;
local_clock_can_slew_ = local_clock_->initCheck() &&
(local_clock_->setLocalSlew(0) == OK);
tgt_correction_ = 0;
cur_correction_ = 0;
// Precompute the max rate at which we are allowed to change the VCXO
// control.
uint64_t N = 0x10000ull * 1000ull;
uint64_t D = local_clock_->getLocalFreq() * kMinFullRangeSlewChange_mSec;
LinearTransform::reduce(&N, &D);
while ((N > INT32_MAX) || (D > UINT32_MAX)) {
N >>= 1;
D >>= 1;
LinearTransform::reduce(&N, &D);
}
time_to_cur_slew_.a_to_b_numer = static_cast<int32_t>(N);
time_to_cur_slew_.a_to_b_denom = static_cast<uint32_t>(D);
reset(true, true);
#ifdef TIME_SERVICE_DEBUG
diag_thread_ = new DiagThread(common_clock_, local_clock_);
if (diag_thread_ != NULL) {
status_t res = diag_thread_->startWorkThread();
if (res != OK)
ALOGW("Failed to start A@H clock recovery diagnostic thread.");
} else
ALOGW("Failed to allocate diagnostic thread.");
#endif
}
ClockRecoveryLoop::~ClockRecoveryLoop() {
#ifdef TIME_SERVICE_DEBUG
diag_thread_->stopWorkThread();
#endif
}
// Constants.
const float ClockRecoveryLoop::dT = 1.0;
const float ClockRecoveryLoop::Kc = 1.0f;
const float ClockRecoveryLoop::Ti = 15.0f;
const float ClockRecoveryLoop::Tf = 0.05;
const float ClockRecoveryLoop::bias_Fc = 0.01;
const float ClockRecoveryLoop::bias_RC = (dT / (2 * 3.14159f * bias_Fc));
const float ClockRecoveryLoop::bias_Alpha = (dT / (bias_RC + dT));
const int64_t ClockRecoveryLoop::panic_thresh_ = 50000;
const int64_t ClockRecoveryLoop::control_thresh_ = 10000;
const float ClockRecoveryLoop::COmin = -100.0f;
const float ClockRecoveryLoop::COmax = 100.0f;
const uint32_t ClockRecoveryLoop::kMinFullRangeSlewChange_mSec = 300;
const int ClockRecoveryLoop::kSlewChangeStepPeriod_mSec = 10;
void ClockRecoveryLoop::reset(bool position, bool frequency) {
Mutex::Autolock lock(&lock_);
reset_l(position, frequency);
}
uint32_t ClockRecoveryLoop::findMinRTTNdx(DisciplineDataPoint* data,
uint32_t count) {
uint32_t min_rtt = 0;
for (uint32_t i = 1; i < count; ++i)
if (data[min_rtt].rtt > data[i].rtt)
min_rtt = i;
return min_rtt;
}
bool ClockRecoveryLoop::pushDisciplineEvent(int64_t local_time,
int64_t nominal_common_time,
int64_t rtt) {
Mutex::Autolock lock(&lock_);
int64_t local_common_time = 0;
common_clock_->localToCommon(local_time, &local_common_time);
int64_t raw_delta = nominal_common_time - local_common_time;
#ifdef TIME_SERVICE_DEBUG
ALOGE("local=%lld, common=%lld, delta=%lld, rtt=%lld\n",
local_common_time, nominal_common_time,
raw_delta, rtt);
#endif
// If we have not defined a basis for common time, then we need to use these
// initial points to do so. In order to avoid significant initial error
// from a particularly bad startup data point, we collect the first N data
// points and choose the best of them before moving on.
if (!common_clock_->isValid()) {
if (startup_filter_wr_ < kStartupFilterSize) {
DisciplineDataPoint& d = startup_filter_data_[startup_filter_wr_];
d.local_time = local_time;
d.nominal_common_time = nominal_common_time;
d.rtt = rtt;
startup_filter_wr_++;
}
if (startup_filter_wr_ == kStartupFilterSize) {
uint32_t min_rtt = findMinRTTNdx(startup_filter_data_,
kStartupFilterSize);
common_clock_->setBasis(
startup_filter_data_[min_rtt].local_time,
startup_filter_data_[min_rtt].nominal_common_time);
}
return true;
}
int64_t observed_common;
int64_t delta;
float delta_f, dCO;
int32_t tgt_correction;
if (OK != common_clock_->localToCommon(local_time, &observed_common)) {
// Since we just checked to make certain that this conversion was valid,
// and no one else in the system should be messing with it, if this
// conversion is suddenly invalid, it is a good reason to panic.
ALOGE("Failed to convert local time to common time in %s:%d",
__PRETTY_FUNCTION__, __LINE__);
return false;
}
// Implement a filter which should match NTP filtering behavior when a
// client is associated with only one peer of lower stratum. Basically,
// always use the best of the N last data points, where best is defined as
// lowest round trip time. NTP uses an N of 8; we use a value of 6.
//
// TODO(johngro) : experiment with other filter strategies. The goal here
// is to mitigate the effects of high RTT data points which typically have
// large asymmetries in the TX/RX legs. Downside of the existing NTP
// approach (particularly because of the PID controller we are using to
// produce the control signal from the filtered data) are that the rate at
// which discipline events are actually acted upon becomes irregular and can
// become drawn out (the time between actionable event can go way up). If
// the system receives a strong high quality data point, the proportional
// component of the controller can produce a strong correction which is left
// in place for too long causing overshoot. In addition, the integral
// component of the system currently is an approximation based on the
// assumption of a more or less homogeneous sampling of the error. Its
// unclear what the effect of undermining this assumption would be right
// now.
// Two ideas which come to mind immediately would be to...
// 1) Keep a history of more data points (32 or so) and ignore data points
// whose RTT is more than a certain number of standard deviations outside
// of the norm.
// 2) Eliminate the PID controller portion of this system entirely.
// Instead, move to a system which uses a very wide filter (128 data
// points or more) with a sum-of-least-squares line fitting approach to
// tracking the long term drift. This would take the place of the I
// component in the current PID controller. Also use a much more narrow
// outlier-rejector filter (as described in #1) to drive a short term
// correction factor similar to the P component of the PID controller.
assert(filter_wr_ < kFilterSize);
filter_data_[filter_wr_].local_time = local_time;
filter_data_[filter_wr_].observed_common_time = observed_common;
filter_data_[filter_wr_].nominal_common_time = nominal_common_time;
filter_data_[filter_wr_].rtt = rtt;
filter_data_[filter_wr_].point_used = false;
uint32_t current_point = filter_wr_;
filter_wr_ = (filter_wr_ + 1) % kFilterSize;
if (!filter_wr_)
filter_full_ = true;
uint32_t scan_end = filter_full_ ? kFilterSize : filter_wr_;
uint32_t min_rtt = findMinRTTNdx(filter_data_, scan_end);
// We only use packets with low RTTs for control. If the packet RTT
// is less than the panic threshold, we can probably eat the jitter with the
// control loop. Otherwise, take the packet only if it better than all
// of the packets we have in the history. That way we try to track
// something, even if it is noisy.
if (current_point == min_rtt || rtt < control_thresh_) {
delta_f = delta = nominal_common_time - observed_common;
last_error_est_valid_ = true;
last_error_est_usec_ = delta;
// Compute the error then clamp to the panic threshold. If we ever
// exceed this amt of error, its time to panic and reset the system.
// Given that the error in the measurement of the error could be as
// high as the RTT of the data point, we don't actually panic until
// the implied error (delta) is greater than the absolute panic
// threashold plus the RTT. IOW - we don't panic until we are
// absoluely sure that our best case sync is worse than the absolute
// panic threshold.
int64_t effective_panic_thresh = panic_thresh_ + rtt;
if ((delta > effective_panic_thresh) ||
(delta < -effective_panic_thresh)) {
// PANIC!!!
reset_l(false, true);
return false;
}
} else {
// We do not have a good packet to look at, but we also do not want to
// free-run the clock at some crazy slew rate. So we guess the
// trajectory of the clock based on the last controller output and the
// estimated bias of our clock against the master.
// The net effect of this is that CO == CObias after some extended
// period of no feedback.
delta_f = last_delta_f_ - dT*(CO - CObias);
delta = delta_f;
}
// Velocity form PI control equation.
dCO = Kc * (1.0f + dT/Ti) * delta_f - Kc * last_delta_f_;
CO += dCO * Tf; // Filter CO by applying gain <1 here.
// Save error terms for later.
last_delta_f_ = delta_f;
// Clamp CO to +/- 100ppm.
if (CO < COmin)
CO = COmin;
else if (CO > COmax)
CO = COmax;
// Update the controller bias.
CObias = bias_Alpha * CO + (1.0f - bias_Alpha) * lastCObias;
lastCObias = CObias;
// Convert PPM to 16-bit int range. Add some guard band (-0.01) so we
// don't get fp weirdness.
tgt_correction = CO * 327.66;
// If there was a change in the amt of correction to use, update the
// system.
setTargetCorrection_l(tgt_correction);
LOG_TS("clock_loop %" PRId64 " %f %f %f %d\n", raw_delta, delta_f, CO, CObias, tgt_correction);
#ifdef TIME_SERVICE_DEBUG
diag_thread_->pushDisciplineEvent(
local_time,
observed_common,
nominal_common_time,
tgt_correction,
rtt);
#endif
return true;
}
int32_t ClockRecoveryLoop::getLastErrorEstimate() {
Mutex::Autolock lock(&lock_);
if (last_error_est_valid_)
return last_error_est_usec_;
else
return ICommonClock::kErrorEstimateUnknown;
}
void ClockRecoveryLoop::reset_l(bool position, bool frequency) {
assert(NULL != common_clock_);
if (position) {
common_clock_->resetBasis();
startup_filter_wr_ = 0;
}
if (frequency) {
last_error_est_valid_ = false;
last_error_est_usec_ = 0;
last_delta_f_ = 0.0;
CO = 0.0f;
lastCObias = CObias = 0.0f;
setTargetCorrection_l(0);
applySlew_l();
}
filter_wr_ = 0;
filter_full_ = false;
}
void ClockRecoveryLoop::setTargetCorrection_l(int32_t tgt) {
// When we make a change to the slew rate, we need to be careful to not
// change it too quickly as it can anger some HDMI sinks out there, notably
// some Sony panels from the 2010-2011 timeframe. From experimenting with
// some of these sinks, it seems like swinging from one end of the range to
// another in less that 190mSec or so can start to cause trouble. Adding in
// a hefty margin, we limit the system to a full range sweep in no less than
// 300mSec.
if (tgt_correction_ != tgt) {
int64_t now = local_clock_->getLocalTime();
tgt_correction_ = tgt;
// Set up the transformation to figure out what the slew should be at
// any given point in time in the future.
time_to_cur_slew_.a_zero = now;
time_to_cur_slew_.b_zero = cur_correction_;
// Make sure the sign of the slope is headed in the proper direction.
bool needs_increase = (cur_correction_ < tgt_correction_);
bool is_increasing = (time_to_cur_slew_.a_to_b_numer > 0);
if (( needs_increase && !is_increasing) ||
(!needs_increase && is_increasing)) {
time_to_cur_slew_.a_to_b_numer = -time_to_cur_slew_.a_to_b_numer;
}
// Finally, figure out when the change will be finished and start the
// slew operation.
time_to_cur_slew_.doReverseTransform(tgt_correction_,
&slew_change_end_time_);
applySlew_l();
}
}
bool ClockRecoveryLoop::applySlew_l() {
bool ret = true;
// If cur == tgt, there is no ongoing sleq rate change and we are already
// finished.
if (cur_correction_ == tgt_correction_)
goto bailout;
if (local_clock_can_slew_) {
int64_t now = local_clock_->getLocalTime();
int64_t tmp;
if (now >= slew_change_end_time_) {
cur_correction_ = tgt_correction_;
next_slew_change_timeout_.setTimeout(-1);
} else {
time_to_cur_slew_.doForwardTransform(now, &tmp);
if (tmp > INT16_MAX)
cur_correction_ = INT16_MAX;
else if (tmp < INT16_MIN)
cur_correction_ = INT16_MIN;
else
cur_correction_ = static_cast<int16_t>(tmp);
next_slew_change_timeout_.setTimeout(kSlewChangeStepPeriod_mSec);
ret = false;
}
local_clock_->setLocalSlew(cur_correction_);
} else {
// Since we are not actually changing the rate of a HW clock, we don't
// need to worry to much about changing the slew rate so fast that we
// anger any downstream HDMI devices.
cur_correction_ = tgt_correction_;
next_slew_change_timeout_.setTimeout(-1);
// The SW clock recovery implemented by the common clock class expects
// values expressed in PPM. CO is in ppm.
common_clock_->setSlew(local_clock_->getLocalTime(), CO);
}
bailout:
return ret;
}
int ClockRecoveryLoop::applyRateLimitedSlew() {
Mutex::Autolock lock(&lock_);
int ret = next_slew_change_timeout_.msecTillTimeout();
if (!ret) {
if (applySlew_l())
next_slew_change_timeout_.setTimeout(-1);
ret = next_slew_change_timeout_.msecTillTimeout();
}
return ret;
}
} // namespace android

View File

@@ -1,159 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __CLOCK_RECOVERY_H__
#define __CLOCK_RECOVERY_H__
#include <stdint.h>
#include <common_time/ICommonClock.h>
#include <utils/threads.h>
#include "LinearTransform.h"
#ifdef TIME_SERVICE_DEBUG
#include "diag_thread.h"
#endif
#include "utils.h"
namespace android {
class CommonClock;
class LocalClock;
class ClockRecoveryLoop {
public:
ClockRecoveryLoop(LocalClock* local_clock, CommonClock* common_clock);
~ClockRecoveryLoop();
void reset(bool position, bool frequency);
bool pushDisciplineEvent(int64_t local_time,
int64_t nominal_common_time,
int64_t data_point_rtt);
int32_t getLastErrorEstimate();
// Applies the next step in any ongoing slew change operation. Returns a
// timeout suitable for use with poll/select indicating the number of mSec
// until the next change should be applied.
int applyRateLimitedSlew();
private:
// Tuned using the "Good Gain" method.
// See:
// http://techteach.no/publications/books/dynamics_and_control/tuning_pid_controller.pdf
// Controller period (1Hz for now).
static const float dT;
// Controller gain, positive and unitless. Larger values converge faster,
// but can cause instability.
static const float Kc;
// Integral reset time. Smaller values cause loop to track faster, but can
// also cause instability.
static const float Ti;
// Controller output filter time constant. Range (0-1). Smaller values make
// output smoother, but slow convergence.
static const float Tf;
// Low-pass filter for bias tracker.
static const float bias_Fc; // HZ
static const float bias_RC; // Computed in constructor.
static const float bias_Alpha; // Computed inconstructor.
// The maximum allowed error (as indicated by a pushDisciplineEvent) before
// we panic.
static const int64_t panic_thresh_;
// The maximum allowed error rtt time for packets to be used for control
// feedback, unless the packet is the best in recent memory.
static const int64_t control_thresh_;
typedef struct {
int64_t local_time;
int64_t observed_common_time;
int64_t nominal_common_time;
int64_t rtt;
bool point_used;
} DisciplineDataPoint;
static uint32_t findMinRTTNdx(DisciplineDataPoint* data, uint32_t count);
void reset_l(bool position, bool frequency);
void setTargetCorrection_l(int32_t tgt);
bool applySlew_l();
// The local clock HW abstraction we use as the basis for common time.
LocalClock* local_clock_;
bool local_clock_can_slew_;
// The common clock we end up controlling along with the lock used to
// serialize operations.
CommonClock* common_clock_;
Mutex lock_;
// parameters maintained while running and reset during a reset
// of the frequency correction.
bool last_error_est_valid_;
int32_t last_error_est_usec_;
float last_delta_f_;
int32_t tgt_correction_;
int32_t cur_correction_;
LinearTransform time_to_cur_slew_;
int64_t slew_change_end_time_;
Timeout next_slew_change_timeout_;
// Contoller Output.
float CO;
// Bias tracking for trajectory estimation.
float CObias;
float lastCObias;
// Controller output bounds. The controller will not try to
// slew faster that +/-100ppm offset from center per interation.
static const float COmin;
static const float COmax;
// State kept for filtering the discipline data.
static const uint32_t kFilterSize = 16;
DisciplineDataPoint filter_data_[kFilterSize];
uint32_t filter_wr_;
bool filter_full_;
static const uint32_t kStartupFilterSize = 4;
DisciplineDataPoint startup_filter_data_[kStartupFilterSize];
uint32_t startup_filter_wr_;
// Minimum number of milliseconds over which we allow a full range change
// (from rail to rail) of the VCXO control signal. This is the rate
// limiting factor which keeps us from changing the clock rate so fast that
// we get in trouble with certain HDMI sinks.
static const uint32_t kMinFullRangeSlewChange_mSec;
// How much time (in msec) to wait
static const int kSlewChangeStepPeriod_mSec;
#ifdef TIME_SERVICE_DEBUG
sp<DiagThread> diag_thread_;
#endif
};
} // namespace android
#endif // __CLOCK_RECOVERY_H__

View File

@@ -1,150 +0,0 @@
/*
* 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.
*/
#define __STDC_LIMIT_MACROS
#define LOG_TAG "common_time"
#include <utils/Log.h>
#include <inttypes.h>
#include <stdint.h>
#include <utils/Errors.h>
#include "common_clock.h"
namespace android {
CommonClock::CommonClock() {
cur_slew_ = 0;
cur_trans_valid_ = false;
cur_trans_.a_zero = 0;
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) {
Mutex::Autolock lock(&lock_);
if (!local_freq)
return false;
uint64_t numer = kCommonFreq;
uint64_t denom = local_freq;
LinearTransform::reduce(&numer, &denom);
if ((numer > UINT32_MAX) || (denom > UINT32_MAX)) {
ALOGE("Overflow in CommonClock::init while trying to reduce %" PRIu64 "/%" PRIu64,
kCommonFreq, local_freq);
return false;
}
cur_trans_.a_to_b_numer = local_to_common_freq_numer_ =
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;
}
status_t CommonClock::localToCommon(int64_t local, int64_t *common_out) const {
Mutex::Autolock lock(&lock_);
if (!cur_trans_valid_)
return INVALID_OPERATION;
if (!cur_trans_.doForwardTransform(local, common_out))
return INVALID_OPERATION;
return OK;
}
status_t CommonClock::commonToLocal(int64_t common, int64_t *local_out) const {
Mutex::Autolock lock(&lock_);
if (!cur_trans_valid_)
return INVALID_OPERATION;
if (!cur_trans_.doReverseTransform(common, local_out))
return INVALID_OPERATION;
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_);
cur_trans_.a_zero = local;
cur_trans_.b_zero = common;
cur_trans_valid_ = true;
}
void CommonClock::resetBasis() {
Mutex::Autolock lock(&lock_);
cur_trans_.a_zero = 0;
cur_trans_.b_zero = 0;
cur_trans_valid_ = false;
}
status_t CommonClock::setSlew(int64_t change_time, int32_t ppm) {
Mutex::Autolock lock(&lock_);
int64_t new_local_basis;
int64_t new_common_basis;
if (cur_trans_valid_) {
new_local_basis = change_time;
if (!cur_trans_.doForwardTransform(change_time, &new_common_basis)) {
ALOGE("Overflow when attempting to set slew rate to %d", ppm);
return INVALID_OPERATION;
}
} else {
new_local_basis = 0;
new_common_basis = 0;
}
cur_slew_ = ppm;
uint32_t n1 = local_to_common_freq_numer_;
uint32_t n2 = 1000000 + cur_slew_;
uint32_t d1 = local_to_common_freq_denom_;
uint32_t d2 = 1000000;
// n1/d1 has already been reduced, no need to do so here.
LinearTransform::reduce(&n1, &d2);
LinearTransform::reduce(&n2, &d1);
LinearTransform::reduce(&n2, &d2);
cur_trans_.a_zero = new_local_basis;
cur_trans_.b_zero = new_common_basis;
cur_trans_.a_to_b_numer = n1 * n2;
cur_trans_.a_to_b_denom = d1 * d2;
return OK;
}
} // namespace android

View File

@@ -1,58 +0,0 @@
/*
* 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 __COMMON_CLOCK_H__
#define __COMMON_CLOCK_H__
#include <stdint.h>
#include <utils/Errors.h>
#include <utils/threads.h>
#include "LinearTransform.h"
namespace android {
class CommonClock {
public:
CommonClock();
bool init(uint64_t local_freq);
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);
void setBasis(int64_t local, int64_t common);
void resetBasis();
private:
mutable Mutex lock_;
int32_t cur_slew_;
uint32_t local_to_common_freq_numer_;
uint32_t local_to_common_freq_denom_;
LinearTransform duration_trans_;
LinearTransform cur_trans_;
bool cur_trans_valid_;
static const uint64_t kCommonFreq = 1000000ull;
};
} // namespace android
#endif // __COMMON_CLOCK_H__

View File

@@ -1,157 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <common_time/local_clock.h>
#include <utils/String8.h>
#include "common_clock_service.h"
#include "common_clock.h"
#include "common_time_server.h"
namespace android {
sp<CommonClockService> CommonClockService::instantiate(
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) {
return mTimeServer.isCommonTimeValid(valid, timelineID);
}
status_t CommonClockService::commonTimeToLocalTime(int64_t commonTime,
int64_t* localTime) {
return mTimeServer.getCommonClock().commonToLocal(commonTime, localTime);
}
status_t CommonClockService::localTimeToCommonTime(int64_t localTime,
int64_t* commonTime) {
return mTimeServer.getCommonClock().localToCommon(localTime, commonTime);
}
status_t CommonClockService::getCommonTime(int64_t* commonTime) {
return localTimeToCommonTime(mTimeServer.getLocalClock().getLocalTime(), commonTime);
}
status_t CommonClockService::getCommonFreq(uint64_t* freq) {
*freq = mTimeServer.getCommonClock().getCommonFreq();
return OK;
}
status_t CommonClockService::getLocalTime(int64_t* localTime) {
*localTime = mTimeServer.getLocalClock().getLocalTime();
return OK;
}
status_t CommonClockService::getLocalFreq(uint64_t* freq) {
*freq = mTimeServer.getLocalClock().getLocalFreq();
return OK;
}
status_t CommonClockService::getEstimatedError(int32_t* estimate) {
*estimate = mTimeServer.getEstimatedError();
return OK;
}
status_t CommonClockService::getTimelineID(uint64_t* id) {
*id = mTimeServer.getTimelineID();
return OK;
}
status_t CommonClockService::getState(State* state) {
*state = mTimeServer.getState();
return OK;
}
status_t CommonClockService::getMasterAddr(struct sockaddr_storage* addr) {
return mTimeServer.getMasterAddr(addr);
}
status_t CommonClockService::registerListener(
const sp<ICommonClockListener>& listener) {
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 (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener))
return ALREADY_EXISTS;
}
}
mListeners.add(listener);
mTimeServer.reevaluateAutoDisableState(0 != mListeners.size());
return IInterface::asBinder(listener)->linkToDeath(this);
}
status_t CommonClockService::unregisterListener(
const sp<ICommonClockListener>& listener) {
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 (IInterface::asBinder(mListeners[i]) == IInterface::asBinder(listener)) {
IInterface::asBinder(mListeners[i])->unlinkToDeath(this);
mListeners.removeAt(i);
ret_val = OK;
break;
}
}
}
mTimeServer.reevaluateAutoDisableState(0 != mListeners.size());
return ret_val;
}
void CommonClockService::binderDied(const wp<IBinder>& who) {
Mutex::Autolock lock(mRegistrationLock);
{ // scoping for autolock pattern
Mutex::Autolock lock(mCallbackLock);
for (size_t i = 0; i < mListeners.size(); i++) {
if (IInterface::asBinder(mListeners[i]) == who) {
mListeners.removeAt(i);
break;
}
}
}
mTimeServer.reevaluateAutoDisableState(0 != mListeners.size());
}
void CommonClockService::notifyOnTimelineChanged(uint64_t timelineID) {
Mutex::Autolock lock(mCallbackLock);
for (size_t i = 0; i < mListeners.size(); i++) {
mListeners[i]->onTimelineChanged(timelineID);
}
}
}; // namespace android

View File

@@ -1,91 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_COMMON_CLOCK_SERVICE_H
#define ANDROID_COMMON_CLOCK_SERVICE_H
#include <sys/socket.h>
#include <common_time/ICommonClock.h>
namespace android {
class CommonTimeServer;
class CommonClockService : public BnCommonClock,
public android::IBinder::DeathRecipient {
public:
static sp<CommonClockService> instantiate(CommonTimeServer& timeServer);
virtual status_t dump(int fd, const Vector<String16>& args);
virtual status_t isCommonTimeValid(bool* valid, uint32_t *timelineID);
virtual status_t commonTimeToLocalTime(int64_t common_time,
int64_t* local_time);
virtual status_t localTimeToCommonTime(int64_t local_time,
int64_t* common_time);
virtual status_t getCommonTime(int64_t* common_time);
virtual status_t getCommonFreq(uint64_t* freq);
virtual status_t getLocalTime(int64_t* local_time);
virtual status_t getLocalFreq(uint64_t* freq);
virtual status_t getEstimatedError(int32_t* estimate);
virtual status_t getTimelineID(uint64_t* id);
virtual status_t getState(ICommonClock::State* state);
virtual status_t getMasterAddr(struct sockaddr_storage* addr);
virtual status_t registerListener(
const sp<ICommonClockListener>& listener);
virtual status_t unregisterListener(
const sp<ICommonClockListener>& listener);
void notifyOnTimelineChanged(uint64_t timelineID);
private:
explicit CommonClockService(CommonTimeServer& timeServer)
: mTimeServer(timeServer) { };
virtual void binderDied(const wp<IBinder>& who);
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;
Vector<sp<ICommonClockListener> > mListeners;
};
}; // namespace android
#endif // ANDROID_COMMON_CLOCK_SERVICE_H

View File

@@ -1,112 +0,0 @@
/*
* 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.
*/
#include <utils/String8.h>
#include "common_time_config_service.h"
#include "common_time_server.h"
namespace android {
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 mTimeServer.getMasterElectionPriority(priority);
}
status_t CommonTimeConfigService::setMasterElectionPriority(uint8_t priority) {
return mTimeServer.setMasterElectionPriority(priority);
}
status_t CommonTimeConfigService::getMasterElectionEndpoint(
struct sockaddr_storage *addr) {
return mTimeServer.getMasterElectionEndpoint(addr);
}
status_t CommonTimeConfigService::setMasterElectionEndpoint(
const struct sockaddr_storage *addr) {
return mTimeServer.setMasterElectionEndpoint(addr);
}
status_t CommonTimeConfigService::getMasterElectionGroupId(uint64_t *id) {
return mTimeServer.getMasterElectionGroupId(id);
}
status_t CommonTimeConfigService::setMasterElectionGroupId(uint64_t id) {
return mTimeServer.setMasterElectionGroupId(id);
}
status_t CommonTimeConfigService::getInterfaceBinding(String16& ifaceName) {
String8 tmp;
status_t ret = mTimeServer.getInterfaceBinding(tmp);
ifaceName = String16(tmp);
return ret;
}
status_t CommonTimeConfigService::setInterfaceBinding(const String16& ifaceName) {
String8 tmp(ifaceName);
return mTimeServer.setInterfaceBinding(tmp);
}
status_t CommonTimeConfigService::getMasterAnnounceInterval(int *interval) {
return mTimeServer.getMasterAnnounceInterval(interval);
}
status_t CommonTimeConfigService::setMasterAnnounceInterval(int interval) {
return mTimeServer.setMasterAnnounceInterval(interval);
}
status_t CommonTimeConfigService::getClientSyncInterval(int *interval) {
return mTimeServer.getClientSyncInterval(interval);
}
status_t CommonTimeConfigService::setClientSyncInterval(int interval) {
return mTimeServer.setClientSyncInterval(interval);
}
status_t CommonTimeConfigService::getPanicThreshold(int *threshold) {
return mTimeServer.getPanicThreshold(threshold);
}
status_t CommonTimeConfigService::setPanicThreshold(int threshold) {
return mTimeServer.setPanicThreshold(threshold);
}
status_t CommonTimeConfigService::getAutoDisable(bool *autoDisable) {
return mTimeServer.getAutoDisable(autoDisable);
}
status_t CommonTimeConfigService::setAutoDisable(bool autoDisable) {
return mTimeServer.setAutoDisable(autoDisable);
}
status_t CommonTimeConfigService::forceNetworklessMasterMode() {
return mTimeServer.forceNetworklessMasterMode();
}
}; // namespace android

View File

@@ -1,60 +0,0 @@
/* * 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_CONFIG_SERVICE_H
#define ANDROID_COMMON_TIME_CONFIG_SERVICE_H
#include <sys/socket.h>
#include <common_time/ICommonTimeConfig.h>
namespace android {
class String16;
class CommonTimeServer;
class CommonTimeConfigService : public BnCommonTimeConfig {
public:
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);
virtual status_t getMasterElectionEndpoint(struct sockaddr_storage *addr);
virtual status_t setMasterElectionEndpoint(const struct sockaddr_storage *addr);
virtual status_t getMasterElectionGroupId(uint64_t *id);
virtual status_t setMasterElectionGroupId(uint64_t id);
virtual status_t getInterfaceBinding(String16& ifaceName);
virtual status_t setInterfaceBinding(const String16& ifaceName);
virtual status_t getMasterAnnounceInterval(int *interval);
virtual status_t setMasterAnnounceInterval(int interval);
virtual status_t getClientSyncInterval(int *interval);
virtual status_t setClientSyncInterval(int interval);
virtual status_t getPanicThreshold(int *threshold);
virtual status_t setPanicThreshold(int threshold);
virtual status_t getAutoDisable(bool *autoDisable);
virtual status_t setAutoDisable(bool autoDisable);
virtual status_t forceNetworklessMasterMode();
private:
explicit CommonTimeConfigService(CommonTimeServer& timeServer)
: mTimeServer(timeServer) { }
CommonTimeServer& mTimeServer;
};
}; // namespace android
#endif // ANDROID_COMMON_TIME_CONFIG_SERVICE_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,324 +0,0 @@
/*
* 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 <sys/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"
#include "utils.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];
};
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
Timeout 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;
// Internal logs used for dumpsys.
LogRing mStateChangeLog;
LogRing mElectionLog;
LogRing mBadPktLog;
// 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 kSetupRetryTimeoutMs;
static const int64_t kNoGoodDataPanicThresholdUsec;
static const uint32_t kRTTDiscardPanicThreshMultiplier;
/*** 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

View File

@@ -1,440 +0,0 @@
/*
* 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"
#include <inttypes.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) {
ALOGI("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 : %" PRId64 "\n",
localTime);
if (synced)
dump_printf("Common time : %" PRId64 "\n", commonTime);
else
dump_printf("Common time : %s\n", "not synced");
dump_printf("Timeline ID : %016" PRIu64 "\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 : %" PRId64 " uSec ago\n", usecDelta);
}
dump_printf("Active Clients : %zu\n", activeClients);
mClient_PacketRTTLog.dumpLog(fd, mCommonClock);
mStateChangeLog.dumpLog(fd);
mElectionLog.dumpLog(fd);
mBadPktLog.dumpLog(fd);
}
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 : %016" PRIu64 "\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 %12" PRId64 " localRX %12" PRId64 " "
"(%.3f msec RTT)\n",
ndx, txTimes[i], rxTimes[i],
static_cast<float>(deltaUsec) / 1000.0);
} else {
dump_printf("pkt[%2d] : localTX %12" PRId64 " localRX never\n",
ndx, txTimes[i]);
}
i = (i + 1) % RTT_LOG_SIZE;
ndx++;
} while (i != wrPtr);
}
#undef dump_printf
#undef checked_percentage
} // namespace android

View File

@@ -1,293 +0,0 @@
/*
* 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

View File

@@ -1,189 +0,0 @@
/*
* 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

View File

@@ -1,323 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "common_time"
#include <utils/Log.h>
#include <fcntl.h>
#include <linux/in.h>
#include <linux/tcp.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <utils/Errors.h>
#include <utils/misc.h>
#include <common_time/local_clock.h>
#include "common_clock.h"
#include "diag_thread.h"
#define kMaxEvents 16
#define kListenPort 9876
static bool setNonblocking(int fd) {
int flags = fcntl(fd, F_GETFL);
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
ALOGE("Failed to set socket (%d) to non-blocking mode (errno %d)",
fd, errno);
return false;
}
return true;
}
static bool setNodelay(int fd) {
int tmp = 1;
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &tmp, sizeof(tmp)) < 0) {
ALOGE("Failed to set socket (%d) to no-delay mode (errno %d)",
fd, errno);
return false;
}
return true;
}
namespace android {
DiagThread::DiagThread(CommonClock* common_clock, LocalClock* local_clock) {
common_clock_ = common_clock;
local_clock_ = local_clock;
listen_fd_ = -1;
data_fd_ = -1;
kernel_logID_basis_known_ = false;
discipline_log_ID_ = 0;
}
DiagThread::~DiagThread() {
}
status_t DiagThread::startWorkThread() {
status_t res;
stopWorkThread();
res = run("Diag");
if (res != OK)
ALOGE("Failed to start work thread (res = %d)", res);
return res;
}
void DiagThread::stopWorkThread() {
status_t res;
res = requestExitAndWait(); // block until thread exit.
if (res != OK)
ALOGE("Failed to stop work thread (res = %d)", res);
}
bool DiagThread::openListenSocket() {
bool ret = false;
int flags;
cleanupListenSocket();
if ((listen_fd_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
ALOGE("Socket failed.");
goto bailout;
}
// Set non-blocking operation
if (!setNonblocking(listen_fd_))
goto bailout;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(kListenPort);
if (bind(listen_fd_, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
ALOGE("Bind failed.");
goto bailout;
}
if (listen(listen_fd_, 1) < 0) {
ALOGE("Listen failed.");
goto bailout;
}
ret = true;
bailout:
if (!ret)
cleanupListenSocket();
return ret;
}
void DiagThread::cleanupListenSocket() {
if (listen_fd_ >= 0) {
int res;
struct linger l;
l.l_onoff = 1;
l.l_linger = 0;
setsockopt(listen_fd_, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
shutdown(listen_fd_, SHUT_RDWR);
close(listen_fd_);
listen_fd_ = -1;
}
}
void DiagThread::cleanupDataSocket() {
if (data_fd_ >= 0) {
int res;
struct linger l;
l.l_onoff = 1;
l.l_linger = 0;
setsockopt(data_fd_, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
shutdown(data_fd_, SHUT_RDWR);
close(data_fd_);
data_fd_ = -1;
}
}
void DiagThread::resetLogIDs() {
// Drain and discard all of the events from the kernel
struct local_time_debug_event events[kMaxEvents];
while(local_clock_->getDebugLog(events, kMaxEvents) > 0)
;
{
Mutex::Autolock lock(&discipline_log_lock_);
discipline_log_.clear();
discipline_log_ID_ = 0;
}
kernel_logID_basis_known_ = false;
}
void DiagThread::pushDisciplineEvent(int64_t observed_local_time,
int64_t observed_common_time,
int64_t nominal_common_time,
int32_t total_correction,
int32_t rtt) {
Mutex::Autolock lock(&discipline_log_lock_);
DisciplineEventRecord evt;
evt.event_id = discipline_log_ID_++;
evt.action_local_time = local_clock_->getLocalTime();
common_clock_->localToCommon(evt.action_local_time,
&evt.action_common_time);
evt.observed_local_time = observed_local_time;
evt.observed_common_time = observed_common_time;
evt.nominal_common_time = nominal_common_time;
evt.total_correction = total_correction;
evt.rtt = rtt;
discipline_log_.push_back(evt);
while (discipline_log_.size() > kMaxDisciplineLogSize)
discipline_log_.erase(discipline_log_.begin());
}
bool DiagThread::threadLoop() {
struct pollfd poll_fds[1];
if (!openListenSocket()) {
ALOGE("Failed to open listen socket");
goto bailout;
}
while (!exitPending()) {
memset(&poll_fds, 0, sizeof(poll_fds));
if (data_fd_ < 0) {
poll_fds[0].fd = listen_fd_;
poll_fds[0].events = POLLIN;
} else {
poll_fds[0].fd = data_fd_;
poll_fds[0].events = POLLRDHUP | POLLIN;
}
int poll_res = poll(poll_fds, NELEM(poll_fds), 50);
if (poll_res < 0) {
ALOGE("Fatal error (%d,%d) while waiting on events",
poll_res, errno);
goto bailout;
}
if (exitPending())
break;
if (poll_fds[0].revents) {
if (poll_fds[0].fd == listen_fd_) {
data_fd_ = accept(listen_fd_, NULL, NULL);
if (data_fd_ < 0) {
ALOGW("Failed accept on socket %d with err %d",
listen_fd_, errno);
} else {
if (!setNonblocking(data_fd_))
cleanupDataSocket();
if (!setNodelay(data_fd_))
cleanupDataSocket();
}
} else
if (poll_fds[0].fd == data_fd_) {
if (poll_fds[0].revents & POLLRDHUP) {
// Connection hung up; time to clean up.
cleanupDataSocket();
} else
if (poll_fds[0].revents & POLLIN) {
uint8_t cmd;
if (read(data_fd_, &cmd, sizeof(cmd)) > 0) {
switch(cmd) {
case 'r':
case 'R':
resetLogIDs();
break;
}
}
}
}
}
struct local_time_debug_event events[kMaxEvents];
int amt = local_clock_->getDebugLog(events, kMaxEvents);
if (amt > 0) {
for (int i = 0; i < amt; i++) {
struct local_time_debug_event& e = events[i];
if (!kernel_logID_basis_known_) {
kernel_logID_basis_ = e.local_timesync_event_id;
kernel_logID_basis_known_ = true;
}
char buf[1024];
int64_t common_time;
status_t res = common_clock_->localToCommon(e.local_time,
&common_time);
snprintf(buf, sizeof(buf), "E,%lld,%lld,%lld,%d\n",
e.local_timesync_event_id - kernel_logID_basis_,
e.local_time,
common_time,
(OK == res) ? 1 : 0);
buf[sizeof(buf) - 1] = 0;
if (data_fd_ >= 0)
write(data_fd_, buf, strlen(buf));
}
}
{ // scope for autolock pattern
Mutex::Autolock lock(&discipline_log_lock_);
while (discipline_log_.size() > 0) {
char buf[1024];
DisciplineEventRecord& e = *discipline_log_.begin();
snprintf(buf, sizeof(buf),
"D,%lld,%lld,%lld,%lld,%lld,%lld,%d,%d\n",
e.event_id,
e.action_local_time,
e.action_common_time,
e.observed_local_time,
e.observed_common_time,
e.nominal_common_time,
e.total_correction,
e.rtt);
buf[sizeof(buf) - 1] = 0;
if (data_fd_ >= 0)
write(data_fd_, buf, strlen(buf));
discipline_log_.erase(discipline_log_.begin());
}
}
}
bailout:
cleanupDataSocket();
cleanupListenSocket();
return false;
}
} // namespace android

View File

@@ -1,76 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __DIAG_THREAD_H__
#define __DIAG_THREAD_H__
#include <utils/List.h>
#include <utils/threads.h>
namespace android {
class CommonClock;
class LocalClock;
class DiagThread : public Thread {
public:
DiagThread(CommonClock* common_clock, LocalClock* local_clock);
~DiagThread();
status_t startWorkThread();
void stopWorkThread();
virtual bool threadLoop();
void pushDisciplineEvent(int64_t observed_local_time,
int64_t observed_common_time,
int64_t nominal_common_time,
int32_t total_correction,
int32_t rtt);
private:
typedef struct {
int64_t event_id;
int64_t action_local_time;
int64_t action_common_time;
int64_t observed_local_time;
int64_t observed_common_time;
int64_t nominal_common_time;
int32_t total_correction;
int32_t rtt;
} DisciplineEventRecord;
bool openListenSocket();
void cleanupListenSocket();
void cleanupDataSocket();
void resetLogIDs();
CommonClock* common_clock_;
LocalClock* local_clock_;
int listen_fd_;
int data_fd_;
int64_t kernel_logID_basis_;
bool kernel_logID_basis_known_;
static const size_t kMaxDisciplineLogSize = 16;
Mutex discipline_log_lock_;
List<DisciplineEventRecord> discipline_log_;
int64_t discipline_log_ID_;
};
} // namespace android
#endif //__ DIAG_THREAD_H__

View File

@@ -1,43 +0,0 @@
/*
* 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() {
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;
}

View File

@@ -1,164 +0,0 @@
/*
* 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.
*/
#define LOG_TAG "common_time"
#include <utils/Log.h>
#include "utils.h"
namespace android {
void Timeout::setTimeout(int msec) {
if (msec < 0) {
mSystemEndTime = 0;
return;
}
mSystemEndTime = systemTime() + (static_cast<nsecs_t>(msec) * 1000000);
}
int Timeout::msecTillTimeout(nsecs_t nowTime) {
if (!mSystemEndTime) {
return -1;
}
if (mSystemEndTime < nowTime) {
return 0;
}
nsecs_t delta = mSystemEndTime - nowTime;
delta += 999999;
delta /= 1000000;
if (delta > 0x7FFFFFFF) {
return 0x7FFFFFFF;
}
return static_cast<int>(delta);
}
LogRing::LogRing(const char* header, size_t entries)
: mSize(entries)
, mWr(0)
, mIsFull(false)
, mHeader(header) {
mRingBuffer = new Entry[mSize];
if (NULL == mRingBuffer)
ALOGE("Failed to allocate log ring with %zu entries.", mSize);
}
LogRing::~LogRing() {
if (NULL != mRingBuffer)
delete[] mRingBuffer;
}
void LogRing::log(int prio, const char* tag, const char* fmt, ...) {
va_list argp;
va_start(argp, fmt);
internalLog(prio, tag, fmt, argp);
va_end(argp);
}
void LogRing::log(const char* fmt, ...) {
va_list argp;
va_start(argp, fmt);
internalLog(0, NULL, fmt, argp);
va_end(argp);
}
void LogRing::internalLog(int prio,
const char* tag,
const char* fmt,
va_list argp) {
if (NULL != mRingBuffer) {
Mutex::Autolock lock(&mLock);
String8 s(String8::formatV(fmt, argp));
Entry* last = NULL;
if (mIsFull || mWr)
last = &(mRingBuffer[(mWr + mSize - 1) % mSize]);
if ((NULL != last) && !last->s.compare(s)) {
gettimeofday(&(last->last_ts), NULL);
++last->count;
} else {
gettimeofday(&mRingBuffer[mWr].first_ts, NULL);
mRingBuffer[mWr].last_ts = mRingBuffer[mWr].first_ts;
mRingBuffer[mWr].count = 1;
mRingBuffer[mWr].s.setTo(s);
mWr = (mWr + 1) % mSize;
if (!mWr)
mIsFull = true;
}
}
if (NULL != tag)
LOG_PRI_VA(prio, tag, fmt, argp);
}
void LogRing::dumpLog(int fd) {
if (NULL == mRingBuffer)
return;
Mutex::Autolock lock(&mLock);
if (!mWr && !mIsFull)
return;
char buf[1024];
int res;
size_t start = mIsFull ? mWr : 0;
size_t count = mIsFull ? mSize : mWr;
static const char* kTimeFmt = "%a %b %d %Y %H:%M:%S";
res = snprintf(buf, sizeof(buf), "\n%s\n", mHeader);
if (res > 0)
write(fd, buf, res);
for (size_t i = 0; i < count; ++i) {
struct tm t;
char timebuf[64];
char repbuf[96];
size_t ndx = (start + i) % mSize;
if (1 != mRingBuffer[ndx].count) {
localtime_r(&mRingBuffer[ndx].last_ts.tv_sec, &t);
strftime(timebuf, sizeof(timebuf), kTimeFmt, &t);
snprintf(repbuf, sizeof(repbuf),
" (repeated %d times, last was %s.%03ld)",
mRingBuffer[ndx].count,
timebuf,
mRingBuffer[ndx].last_ts.tv_usec / 1000);
repbuf[sizeof(repbuf) - 1] = 0;
} else {
repbuf[0] = 0;
}
localtime_r(&mRingBuffer[ndx].first_ts.tv_sec, &t);
strftime(timebuf, sizeof(timebuf), kTimeFmt, &t);
res = snprintf(buf, sizeof(buf), "[%2zu] %s.%03ld :: %s%s\n",
i, timebuf,
mRingBuffer[ndx].first_ts.tv_usec / 1000,
mRingBuffer[ndx].s.string(),
repbuf);
if (res > 0)
write(fd, buf, res);
}
}
} // namespace android

View File

@@ -1,83 +0,0 @@
/*
* 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 __UTILS_H__
#define __UTILS_H__
#include <stdint.h>
#include <unistd.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <utils/Timers.h>
namespace android {
class Timeout {
public:
Timeout() : mSystemEndTime(0) { }
// Set a timeout which should occur msec milliseconds from now.
// Negative values will cancel any current timeout;
void setTimeout(int msec);
// Return the number of milliseconds until the timeout occurs, or -1 if
// no timeout is scheduled.
int msecTillTimeout(nsecs_t nowTime);
int msecTillTimeout() { return msecTillTimeout(systemTime()); }
private:
// The systemTime() at which the timeout will be complete, or 0 if no
// timeout is currently scheduled.
nsecs_t mSystemEndTime;
};
class LogRing {
public:
LogRing(const char* header, size_t entries);
~LogRing();
// Send a log message to logcat as well as storing it in the ring buffer.
void log(int prio, const char* tag, const char* fmt, ...);
// Add a log message the ring buffer, do not send the message to logcat.
void log(const char* fmt, ...);
// Dump the log to an fd (dumpsys style)
void dumpLog(int fd);
private:
class Entry {
public:
uint32_t count;
struct timeval first_ts;
struct timeval last_ts;
String8 s;
};
Mutex mLock;
Entry* mRingBuffer;
size_t mSize;
size_t mWr;
bool mIsFull;
const char* mHeader;
void internalLog(int prio, const char* tag, const char* fmt, va_list va);
};
} // namespace android
#endif // __UTILS_H__

View File

@@ -1,364 +0,0 @@
/*
* 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.
*/
package com.android.server;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.InterfaceConfiguration;
import android.os.Binder;
import android.os.CommonTimeConfig;
import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.util.Log;
import com.android.internal.util.DumpUtils;
import com.android.server.net.BaseNetworkObserver;
/**
* @hide
* <p>CommonTimeManagementService manages the configuration of the native Common Time service,
* reconfiguring the native service as appropriate in response to changes in network configuration.
*/
class CommonTimeManagementService extends Binder {
/*
* Constants and globals.
*/
private static final String TAG = CommonTimeManagementService.class.getSimpleName();
private static final int NATIVE_SERVICE_RECONNECT_TIMEOUT = 5000;
private static final String AUTO_DISABLE_PROP = "ro.common_time.auto_disable";
private static final String ALLOW_WIFI_PROP = "ro.common_time.allow_wifi";
private static final String SERVER_PRIO_PROP = "ro.common_time.server_prio";
private static final String NO_INTERFACE_TIMEOUT_PROP = "ro.common_time.no_iface_timeout";
private static final boolean AUTO_DISABLE;
private static final boolean ALLOW_WIFI;
private static final byte BASE_SERVER_PRIO;
private static final int NO_INTERFACE_TIMEOUT;
private static final InterfaceScoreRule[] IFACE_SCORE_RULES;
static {
int tmp;
AUTO_DISABLE = (0 != SystemProperties.getInt(AUTO_DISABLE_PROP, 1));
ALLOW_WIFI = (0 != SystemProperties.getInt(ALLOW_WIFI_PROP, 0));
tmp = SystemProperties.getInt(SERVER_PRIO_PROP, 1);
NO_INTERFACE_TIMEOUT = SystemProperties.getInt(NO_INTERFACE_TIMEOUT_PROP, 60000);
if (tmp < 1)
BASE_SERVER_PRIO = 1;
else
if (tmp > 30)
BASE_SERVER_PRIO = 30;
else
BASE_SERVER_PRIO = (byte)tmp;
if (ALLOW_WIFI) {
IFACE_SCORE_RULES = new InterfaceScoreRule[] {
new InterfaceScoreRule("wlan", (byte)1),
new InterfaceScoreRule("eth", (byte)2),
};
} else {
IFACE_SCORE_RULES = new InterfaceScoreRule[] {
new InterfaceScoreRule("eth", (byte)2),
};
}
};
/*
* Internal state
*/
private final Context mContext;
private final Object mLock = new Object();
private INetworkManagementService mNetMgr;
private CommonTimeConfig mCTConfig;
private String mCurIface;
private Handler mReconnectHandler = new Handler();
private Handler mNoInterfaceHandler = new Handler();
private boolean mDetectedAtStartup = false;
private byte mEffectivePrio = BASE_SERVER_PRIO;
/*
* Callback handler implementations.
*/
private INetworkManagementEventObserver mIfaceObserver = new BaseNetworkObserver() {
@Override
public void interfaceStatusChanged(String iface, boolean up) {
reevaluateServiceState();
}
@Override
public void interfaceLinkStateChanged(String iface, boolean up) {
reevaluateServiceState();
}
@Override
public void interfaceAdded(String iface) {
reevaluateServiceState();
}
@Override
public void interfaceRemoved(String iface) {
reevaluateServiceState();
}
};
private BroadcastReceiver mConnectivityMangerObserver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
reevaluateServiceState();
}
};
private CommonTimeConfig.OnServerDiedListener mCTServerDiedListener =
() -> scheduleTimeConfigReconnect();
private Runnable mReconnectRunnable = () -> connectToTimeConfig();
private Runnable mNoInterfaceRunnable = () -> handleNoInterfaceTimeout();
/*
* Public interface (constructor, systemReady and dump)
*/
public CommonTimeManagementService(Context context) {
mContext = context;
}
void systemRunning() {
if (ServiceManager.checkService(CommonTimeConfig.SERVICE_NAME) == null) {
Log.i(TAG, "No common time service detected on this platform. " +
"Common time services will be unavailable.");
return;
}
mDetectedAtStartup = true;
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
mNetMgr = INetworkManagementService.Stub.asInterface(b);
// Network manager is running along-side us, so we should never receiver a remote exception
// while trying to register this observer.
try {
mNetMgr.registerObserver(mIfaceObserver);
}
catch (RemoteException e) { }
// Register with the connectivity manager for connectivity changed intents.
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mContext.registerReceiver(mConnectivityMangerObserver, filter);
// Connect to the common time config service and apply the initial configuration.
connectToTimeConfig();
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
if (!mDetectedAtStartup) {
pw.println("Native Common Time service was not detected at startup. " +
"Service is unavailable");
return;
}
synchronized (mLock) {
pw.println("Current Common Time Management Service Config:");
pw.println(String.format(" Native service : %s",
(null == mCTConfig) ? "reconnecting"
: "alive"));
pw.println(String.format(" Bound interface : %s",
(null == mCurIface ? "unbound" : mCurIface)));
pw.println(String.format(" Allow WiFi : %s", ALLOW_WIFI ? "yes" : "no"));
pw.println(String.format(" Allow Auto Disable : %s", AUTO_DISABLE ? "yes" : "no"));
pw.println(String.format(" Server Priority : %d", mEffectivePrio));
pw.println(String.format(" No iface timeout : %d", NO_INTERFACE_TIMEOUT));
}
}
/*
* Inner helper classes
*/
private static class InterfaceScoreRule {
public final String mPrefix;
public final byte mScore;
public InterfaceScoreRule(String prefix, byte score) {
mPrefix = prefix;
mScore = score;
}
};
/*
* Internal implementation
*/
private void cleanupTimeConfig() {
mReconnectHandler.removeCallbacks(mReconnectRunnable);
mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable);
if (null != mCTConfig) {
mCTConfig.release();
mCTConfig = null;
}
}
private void connectToTimeConfig() {
// Get access to the common time service configuration interface. If we catch a remote
// exception in the process (service crashed or no running for w/e reason), schedule an
// attempt to reconnect in the future.
cleanupTimeConfig();
try {
synchronized (mLock) {
mCTConfig = new CommonTimeConfig();
mCTConfig.setServerDiedListener(mCTServerDiedListener);
mCurIface = mCTConfig.getInterfaceBinding();
mCTConfig.setAutoDisable(AUTO_DISABLE);
mCTConfig.setMasterElectionPriority(mEffectivePrio);
}
if (NO_INTERFACE_TIMEOUT >= 0)
mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT);
reevaluateServiceState();
}
catch (RemoteException e) {
scheduleTimeConfigReconnect();
}
}
private void scheduleTimeConfigReconnect() {
cleanupTimeConfig();
Log.w(TAG, String.format("Native service died, will reconnect in %d mSec",
NATIVE_SERVICE_RECONNECT_TIMEOUT));
mReconnectHandler.postDelayed(mReconnectRunnable,
NATIVE_SERVICE_RECONNECT_TIMEOUT);
}
private void handleNoInterfaceTimeout() {
if (null != mCTConfig) {
Log.i(TAG, "Timeout waiting for interface to come up. " +
"Forcing networkless master mode.");
if (CommonTimeConfig.ERROR_DEAD_OBJECT == mCTConfig.forceNetworklessMasterMode())
scheduleTimeConfigReconnect();
}
}
private void reevaluateServiceState() {
String bindIface = null;
byte bestScore = -1;
try {
// Check to see if this interface is suitable to use for time synchronization.
//
// TODO : This selection algorithm needs to be enhanced for use with mobile devices. In
// particular, the choice of whether to a wireless interface or not should not be an all
// or nothing thing controlled by properties. It would probably be better if the
// platform had some concept of public wireless networks vs. home or friendly wireless
// networks (something a user would configure in settings or when a new interface is
// added). Then this algorithm could pick only wireless interfaces which were flagged
// as friendly, and be dormant when on public wireless networks.
//
// Another issue which needs to be dealt with is the use of driver supplied interface
// name to determine the network type. The fact that the wireless interface on a device
// is named "wlan0" is just a matter of convention; its not a 100% rule. For example,
// there are devices out there where the wireless is name "tiwlan0", not "wlan0". The
// internal network management interfaces in Android have all of the information needed
// to make a proper classification, there is just no way (currently) to fetch an
// interface's type (available from the ConnectionManager) as well as its address
// (available from either the java.net interfaces or from the NetworkManagment service).
// Both can enumerate interfaces, but that is no way to correlate their results (no
// common shared key; although using the interface name in the connection manager would
// be a good start). Until this gets resolved, we resort to substring searching for
// tags like wlan and eth.
//
String ifaceList[] = mNetMgr.listInterfaces();
if (null != ifaceList) {
for (String iface : ifaceList) {
byte thisScore = -1;
for (InterfaceScoreRule r : IFACE_SCORE_RULES) {
if (iface.contains(r.mPrefix)) {
thisScore = r.mScore;
break;
}
}
if (thisScore <= bestScore)
continue;
InterfaceConfiguration config = mNetMgr.getInterfaceConfig(iface);
if (null == config)
continue;
if (config.isActive()) {
bindIface = iface;
bestScore = thisScore;
}
}
}
}
catch (RemoteException e) {
// Bad news; we should not be getting remote exceptions from the connectivity manager
// since it is running in SystemServer along side of us. It probably does not matter
// what we do here, but go ahead and unbind the common time service in this case, just
// so we have some defined behavior.
bindIface = null;
}
boolean doRebind = true;
synchronized (mLock) {
if ((null != bindIface) && (null == mCurIface)) {
Log.e(TAG, String.format("Binding common time service to %s.", bindIface));
mCurIface = bindIface;
} else
if ((null == bindIface) && (null != mCurIface)) {
Log.e(TAG, "Unbinding common time service.");
mCurIface = null;
} else
if ((null != bindIface) && (null != mCurIface) && !bindIface.equals(mCurIface)) {
Log.e(TAG, String.format("Switching common time service binding from %s to %s.",
mCurIface, bindIface));
mCurIface = bindIface;
} else {
doRebind = false;
}
}
if (doRebind && (null != mCTConfig)) {
byte newPrio = (bestScore > 0)
? (byte)(bestScore * BASE_SERVER_PRIO)
: BASE_SERVER_PRIO;
if (newPrio != mEffectivePrio) {
mEffectivePrio = newPrio;
mCTConfig.setMasterElectionPriority(mEffectivePrio);
}
int res = mCTConfig.setNetworkBinding(mCurIface);
if (res != CommonTimeConfig.SUCCESS)
scheduleTimeConfigReconnect();
else if (NO_INTERFACE_TIMEOUT >= 0) {
mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable);
if (null == mCurIface)
mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT);
}
}
}
}

View File

@@ -690,7 +690,6 @@ public final class SystemServer {
WindowManagerService wm = null;
SerialService serial = null;
NetworkTimeUpdateService networkTimeUpdater = null;
CommonTimeManagementService commonTimeMgmtService = null;
InputManagerService inputManager = null;
TelephonyRegistry telephonyRegistry = null;
ConsumerIrService consumerIr = null;
@@ -1383,15 +1382,6 @@ public final class SystemServer {
traceEnd();
}
traceBeginAndSlog("StartCommonTimeManagementService");
try {
commonTimeMgmtService = new CommonTimeManagementService(context);
ServiceManager.addService("commontime_management", commonTimeMgmtService);
} catch (Throwable e) {
reportWtf("starting CommonTimeManagementService service", e);
}
traceEnd();
if (!disableNetwork) {
traceBeginAndSlog("CertBlacklister");
try {
@@ -1667,7 +1657,6 @@ public final class SystemServer {
final LocationManagerService locationF = location;
final CountryDetectorService countryDetectorF = countryDetector;
final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
final CommonTimeManagementService commonTimeMgmtServiceF = commonTimeMgmtService;
final InputManagerService inputManagerF = inputManager;
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
final MediaRouterService mediaRouterF = mediaRouter;
@@ -1813,15 +1802,6 @@ public final class SystemServer {
reportWtf("Notifying NetworkTimeService running", e);
}
traceEnd();
traceBeginAndSlog("MakeCommonTimeManagementServiceReady");
try {
if (commonTimeMgmtServiceF != null) {
commonTimeMgmtServiceF.systemRunning();
}
} catch (Throwable e) {
reportWtf("Notifying CommonTimeManagementService running", e);
}
traceEnd();
traceBeginAndSlog("MakeInputManagerServiceReady");
try {
// TODO(BT) Pass parameter to input manager