/*
 * 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
/** * 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 getTime() and getEstimatedError() when the common time service is not able to determine the current common time due to a lack of synchronization.
/** * 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 getTimelineId() when the common time service is not currently synced to any timeline.
/** * 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 getEstimatedError() when the common time service is not currently synced to any timeline.
/** * 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 getState() to indicate that there was an internal error while attempting to determine the state of the common time service.
/** * 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 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 STATE_CLIENT if it finds an active master, or to STATE_MASTER if no active master is found and this client becomes the master of a new timeline.
/** * 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 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.
/** * 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 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.
/** * 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 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 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 STATE_MASTER state. During the election, all clients will allow their timeline to drift without applying correction.
/** * 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 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 STATE_CLIENT state. If no new master announces itself within the timeout threshold, the time service will transition back to the STATE_RONIN state in order to restart the election.
/** * 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
/** * Name of the underlying native binder service */
public static final String SERVICE_NAME = "common_time.clock";
Class constructor.
Throws:
  • RemoteException –
/** * 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.
/** * 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 CommonClock instance. Once resources have been released, the CommonClock instance is disconnected from the native service and will throw a 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.
/** * 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.
Throws:
Returns:a signed 64-bit value representing the current common time in microseconds, or the special value TIME_NOT_SYNCED if the common time service is currently not synchronized.
/** * 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.
Throws:
Returns:a signed 32-bit value representing the common time service's estimation of synchronization accuracy in microseconds, or the special value 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)
/** * 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.
Throws:
Returns:a long representing the unique ID of the timeline the common time service is currently synchronizing with, or INVALID_TIMELINE_ID if the common time service is currently not synchronized.
/** * 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.
Throws:
Returns:a integer indicating the current state of the this clock's common time service in the master election algorithm or STATE_INVALID if there is an internal error.
/** * 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.
Throws:
Returns:an InetSocketAddress containing the IP address and UDP port of the current timeline master, or null if there is no current master.
/** * 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 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 CommonClock.setTimelineChangedListener(OnTimelineChangedListener) method.
/** * 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.
Params:
  • newTimelineId – a long which uniquely identifies the timeline the time synchronization service is now a member of, or CommonClock.INVALID_TIMELINE_ID if the the service is not synchronized to any timeline.
/** * 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.

Call this method with a null listener to stop receiving server death notifications.

/** * 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 CommonClock instance to indicate that the connection to the native media server has been broken and that the CommonClock instance will need to be released and re-created. The client application can implement this interface and register the listener with the CommonClock.setServerDiedListener(OnServerDiedListener) method.
/** * 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.

If the native common time service encounters a fatal error and needs to restart, the binder connection from the CommonClock instance to the common time service will be broken. To restore functionality, clients should CommonClock.release() their old visualizer and create a new instance.

/** * 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.

Call this method with a null listener to stop receiving server death notifications.

/** * 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; }