All Downloads are FREE. Search and download functionalities are using the official Maven repository.

android.os.CommonClock Maven / Gradle / Ivy

Go to download

A library jar that provides APIs for Applications written for the Google Android Platform.

There is a newer version: 14-robolectric-10818077
Show newest version
/*
 * 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.
     * 

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.

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





© 2015 - 2024 Weber Informatics LLC | Privacy Policy