Home | History | Annotate | Download | only in os
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.os;
     17 
     18 import java.net.InetSocketAddress;
     19 import java.util.NoSuchElementException;
     20 import android.os.Binder;
     21 import android.os.CommonTimeUtils;
     22 import android.os.IBinder;
     23 import android.os.Parcel;
     24 import android.os.RemoteException;
     25 import android.os.ServiceManager;
     26 
     27 /**
     28  * Used for accessing the android common time service's common clock and receiving notifications
     29  * about common time synchronization status changes.
     30  * @hide
     31  */
     32 public class CommonClock {
     33     /**
     34      * Sentinel value returned by {@link #getTime()} and {@link #getEstimatedError()} when the
     35      * common time service is not able to determine the current common time due to a lack of
     36      * synchronization.
     37      */
     38     public static final long TIME_NOT_SYNCED = -1;
     39 
     40     /**
     41      * Sentinel value returned by {@link #getTimelineId()} when the common time service is not
     42      * currently synced to any timeline.
     43      */
     44     public static final long INVALID_TIMELINE_ID = 0;
     45 
     46     /**
     47      * Sentinel value returned by {@link #getEstimatedError()} when the common time service is not
     48      * currently synced to any timeline.
     49      */
     50     public static final int ERROR_ESTIMATE_UNKNOWN = 0x7FFFFFFF;
     51 
     52     /**
     53      * Value used by {@link #getState()} to indicate that there was an internal error while
     54      * attempting to determine the state of the common time service.
     55      */
     56     public static final int STATE_INVALID = -1;
     57 
     58     /**
     59      * Value used by {@link #getState()} to indicate that the common time service is in its initial
     60      * state and attempting to find the current timeline master, if any.  The service will
     61      * transition to either {@link #STATE_CLIENT} if it finds an active master, or to
     62      * {@link #STATE_MASTER} if no active master is found and this client becomes the master of a
     63      * new timeline.
     64      */
     65     public static final int STATE_INITIAL = 0;
     66 
     67     /**
     68      * Value used by {@link #getState()} to indicate that the common time service is in its client
     69      * state and is synchronizing its time to a different timeline master on the network.
     70      */
     71     public static final int STATE_CLIENT = 1;
     72 
     73     /**
     74      * Value used by {@link #getState()} to indicate that the common time service is in its master
     75      * state and is serving as the timeline master for other common time service clients on the
     76      * network.
     77      */
     78     public static final int STATE_MASTER = 2;
     79 
     80     /**
     81      * Value used by {@link #getState()} to indicate that the common time service is in its Ronin
     82      * state.  Common time service instances in the client state enter the Ronin state after their
     83      * timeline master becomes unreachable on the network.  Common time services who enter the Ronin
     84      * state will begin a new master election for the timeline they were recently clients of.  As
     85      * clients detect they are not the winner and drop out of the election, they will transition to
     86      * the {@link #STATE_WAIT_FOR_ELECTION} state.  When there is only one client remaining in the
     87      * election, it will assume ownership of the timeline and transition to the
     88      * {@link #STATE_MASTER} state.  During the election, all clients will allow their timeline to
     89      * drift without applying correction.
     90      */
     91     public static final int STATE_RONIN = 3;
     92 
     93     /**
     94      * Value used by {@link #getState()} to indicate that the common time service is waiting for a
     95      * master election to conclude and for the new master to announce itself before transitioning to
     96      * the {@link #STATE_CLIENT} state.  If no new master announces itself within the timeout
     97      * threshold, the time service will transition back to the {@link #STATE_RONIN} state in order
     98      * to restart the election.
     99      */
    100     public static final int STATE_WAIT_FOR_ELECTION = 4;
    101 
    102     /**
    103      * Name of the underlying native binder service
    104      */
    105     public static final String SERVICE_NAME = "common_time.clock";
    106 
    107     /**
    108      * Class constructor.
    109      * @throws android.os.RemoteException
    110      */
    111     public CommonClock()
    112     throws RemoteException {
    113         mRemote = ServiceManager.getService(SERVICE_NAME);
    114         if (null == mRemote)
    115             throw new RemoteException();
    116 
    117         mInterfaceDesc = mRemote.getInterfaceDescriptor();
    118         mUtils = new CommonTimeUtils(mRemote, mInterfaceDesc);
    119         mRemote.linkToDeath(mDeathHandler, 0);
    120         registerTimelineChangeListener();
    121     }
    122 
    123     /**
    124      * Handy class factory method.
    125      */
    126     static public CommonClock create() {
    127         CommonClock retVal;
    128 
    129         try {
    130             retVal = new CommonClock();
    131         }
    132         catch (RemoteException e) {
    133             retVal = null;
    134         }
    135 
    136         return retVal;
    137     }
    138 
    139     /**
    140      * Release all native resources held by this {@link android.os.CommonClock} instance.  Once
    141      * resources have been released, the {@link android.os.CommonClock} instance is disconnected from
    142      * the native service and will throw a {@link android.os.RemoteException} if any of its
    143      * methods are called.  Clients should always call release on their client instances before
    144      * releasing their last Java reference to the instance.  Failure to do this will cause
    145      * non-deterministic native resource reclamation and may cause the common time service to remain
    146      * active on the network for longer than it should.
    147      */
    148     public void release() {
    149         unregisterTimelineChangeListener();
    150         if (null != mRemote) {
    151             try {
    152                 mRemote.unlinkToDeath(mDeathHandler, 0);
    153             }
    154             catch (NoSuchElementException e) { }
    155             mRemote = null;
    156         }
    157         mUtils = null;
    158     }
    159 
    160     /**
    161      * Gets the common clock's current time.
    162      *
    163      * @return a signed 64-bit value representing the current common time in microseconds, or the
    164      * special value {@link #TIME_NOT_SYNCED} if the common time service is currently not
    165      * synchronized.
    166      * @throws android.os.RemoteException
    167      */
    168     public long getTime()
    169     throws RemoteException {
    170         throwOnDeadServer();
    171         return mUtils.transactGetLong(METHOD_GET_COMMON_TIME, TIME_NOT_SYNCED);
    172     }
    173 
    174     /**
    175      * Gets the current estimation of common clock's synchronization accuracy from the common time
    176      * service.
    177      *
    178      * @return a signed 32-bit value representing the common time service's estimation of
    179      * synchronization accuracy in microseconds, or the special value
    180      * {@link #ERROR_ESTIMATE_UNKNOWN} if the common time service is currently not synchronized.
    181      * Negative values indicate that the local server estimates that the nominal common time is
    182      * behind the local server's time (in other words, the local clock is running fast) Positive
    183      * values indicate that the local server estimates that the nominal common time is ahead of the
    184      * local server's time (in other words, the local clock is running slow)
    185      * @throws android.os.RemoteException
    186      */
    187     public int getEstimatedError()
    188     throws RemoteException {
    189         throwOnDeadServer();
    190         return mUtils.transactGetInt(METHOD_GET_ESTIMATED_ERROR, ERROR_ESTIMATE_UNKNOWN);
    191     }
    192 
    193     /**
    194      * Gets the ID of the timeline the common time service is currently synchronizing its clock to.
    195      *
    196      * @return a long representing the unique ID of the timeline the common time service is
    197      * currently synchronizing with, or {@link #INVALID_TIMELINE_ID} if the common time service is
    198      * currently not synchronized.
    199      * @throws android.os.RemoteException
    200      */
    201     public long getTimelineId()
    202     throws RemoteException {
    203         throwOnDeadServer();
    204         return mUtils.transactGetLong(METHOD_GET_TIMELINE_ID, INVALID_TIMELINE_ID);
    205     }
    206 
    207     /**
    208      * Gets the current state of this clock's common time service in the the master election
    209      * algorithm.
    210      *
    211      * @return a integer indicating the current state of the this clock's common time service in the
    212      * master election algorithm or {@link #STATE_INVALID} if there is an internal error.
    213      * @throws android.os.RemoteException
    214      */
    215     public int getState()
    216     throws RemoteException {
    217         throwOnDeadServer();
    218         return mUtils.transactGetInt(METHOD_GET_STATE, STATE_INVALID);
    219     }
    220 
    221     /**
    222      * Gets the IP address and UDP port of the current timeline master.
    223      *
    224      * @return an InetSocketAddress containing the IP address and UDP port of the current timeline
    225      * master, or null if there is no current master.
    226      * @throws android.os.RemoteException
    227      */
    228     public InetSocketAddress getMasterAddr()
    229     throws RemoteException {
    230         throwOnDeadServer();
    231         return mUtils.transactGetSockaddr(METHOD_GET_MASTER_ADDRESS);
    232     }
    233 
    234     /**
    235      * The OnTimelineChangedListener interface defines a method called by the
    236      * {@link android.os.CommonClock} instance to indicate that the time synchronization service has
    237      * either synchronized with a new timeline, or is no longer a member of any timeline.  The
    238      * client application can implement this interface and register the listener with the
    239      * {@link #setTimelineChangedListener(OnTimelineChangedListener)} method.
    240      */
    241     public interface OnTimelineChangedListener  {
    242         /**
    243          * Method called when the time service's timeline has changed.
    244          *
    245          * @param newTimelineId a long which uniquely identifies the timeline the time
    246          * synchronization service is now a member of, or {@link #INVALID_TIMELINE_ID} if the the
    247          * service is not synchronized to any timeline.
    248          */
    249         void onTimelineChanged(long newTimelineId);
    250     }
    251 
    252     /**
    253      * Registers an OnTimelineChangedListener interface.
    254      * <p>Call this method with a null listener to stop receiving server death notifications.
    255      */
    256     public void setTimelineChangedListener(OnTimelineChangedListener listener) {
    257         synchronized (mListenerLock) {
    258             mTimelineChangedListener = listener;
    259         }
    260     }
    261 
    262     /**
    263      * The OnServerDiedListener interface defines a method called by the
    264      * {@link android.os.CommonClock} instance to indicate that the connection to the native media
    265      * server has been broken and that the {@link android.os.CommonClock} instance will need to be
    266      * released and re-created.  The client application can implement this interface and register
    267      * the listener with the {@link #setServerDiedListener(OnServerDiedListener)} method.
    268      */
    269     public interface OnServerDiedListener  {
    270         /**
    271          * Method called when the native media server has died.  <p>If the native common time
    272          * service encounters a fatal error and needs to restart, the binder connection from the
    273          * {@link android.os.CommonClock} instance to the common time service will be broken.  To
    274          * restore functionality, clients should {@link #release()} their old visualizer and create
    275          * a new instance.
    276          */
    277         void onServerDied();
    278     }
    279 
    280     /**
    281      * Registers an OnServerDiedListener interface.
    282      * <p>Call this method with a null listener to stop receiving server death notifications.
    283      */
    284     public void setServerDiedListener(OnServerDiedListener listener) {
    285         synchronized (mListenerLock) {
    286             mServerDiedListener = listener;
    287         }
    288     }
    289 
    290     protected void finalize() throws Throwable { release(); }
    291 
    292     private void throwOnDeadServer() throws RemoteException {
    293         if ((null == mRemote) || (null == mUtils))
    294             throw new RemoteException();
    295     }
    296 
    297     private final Object mListenerLock = new Object();
    298     private OnTimelineChangedListener mTimelineChangedListener = null;
    299     private OnServerDiedListener mServerDiedListener = null;
    300 
    301     private IBinder mRemote = null;
    302     private String mInterfaceDesc = "";
    303     private CommonTimeUtils mUtils;
    304 
    305     private IBinder.DeathRecipient mDeathHandler = new IBinder.DeathRecipient() {
    306         public void binderDied() {
    307             synchronized (mListenerLock) {
    308                 if (null != mServerDiedListener)
    309                     mServerDiedListener.onServerDied();
    310             }
    311         }
    312     };
    313 
    314     private class TimelineChangedListener extends Binder {
    315         @Override
    316         protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
    317         throws RemoteException {
    318             switch (code) {
    319                 case METHOD_CBK_ON_TIMELINE_CHANGED:
    320                     data.enforceInterface(DESCRIPTOR);
    321                     long timelineId = data.readLong();
    322                     synchronized (mListenerLock) {
    323                         if (null != mTimelineChangedListener)
    324                             mTimelineChangedListener.onTimelineChanged(timelineId);
    325                     }
    326                     return true;
    327             }
    328 
    329             return super.onTransact(code, data, reply, flags);
    330         }
    331 
    332         private static final String DESCRIPTOR = "android.os.ICommonClockListener";
    333     };
    334 
    335     private TimelineChangedListener mCallbackTgt = null;
    336 
    337     private void registerTimelineChangeListener() throws RemoteException {
    338         if (null != mCallbackTgt)
    339             return;
    340 
    341         boolean success = false;
    342         android.os.Parcel data  = android.os.Parcel.obtain();
    343         android.os.Parcel reply = android.os.Parcel.obtain();
    344         mCallbackTgt = new TimelineChangedListener();
    345 
    346         try {
    347             data.writeInterfaceToken(mInterfaceDesc);
    348             data.writeStrongBinder(mCallbackTgt);
    349             mRemote.transact(METHOD_REGISTER_LISTENER, data, reply, 0);
    350             success = (0 == reply.readInt());
    351         }
    352         catch (RemoteException e) {
    353             success = false;
    354         }
    355         finally {
    356             reply.recycle();
    357             data.recycle();
    358         }
    359 
    360         // Did we catch a remote exception or fail to register our callback target?  If so, our
    361         // object must already be dead (or be as good as dead).  Clear out all of our state so that
    362         // our other methods will properly indicate a dead object.
    363         if (!success) {
    364             mCallbackTgt = null;
    365             mRemote = null;
    366             mUtils = null;
    367         }
    368     }
    369 
    370     private void unregisterTimelineChangeListener() {
    371         if (null == mCallbackTgt)
    372             return;
    373 
    374         android.os.Parcel data  = android.os.Parcel.obtain();
    375         android.os.Parcel reply = android.os.Parcel.obtain();
    376 
    377         try {
    378             data.writeInterfaceToken(mInterfaceDesc);
    379             data.writeStrongBinder(mCallbackTgt);
    380             mRemote.transact(METHOD_UNREGISTER_LISTENER, data, reply, 0);
    381         }
    382         catch (RemoteException e) { }
    383         finally {
    384             reply.recycle();
    385             data.recycle();
    386             mCallbackTgt = null;
    387         }
    388     }
    389 
    390     private static final int METHOD_IS_COMMON_TIME_VALID = IBinder.FIRST_CALL_TRANSACTION;
    391     private static final int METHOD_COMMON_TIME_TO_LOCAL_TIME = METHOD_IS_COMMON_TIME_VALID + 1;
    392     private static final int METHOD_LOCAL_TIME_TO_COMMON_TIME = METHOD_COMMON_TIME_TO_LOCAL_TIME + 1;
    393     private static final int METHOD_GET_COMMON_TIME = METHOD_LOCAL_TIME_TO_COMMON_TIME + 1;
    394     private static final int METHOD_GET_COMMON_FREQ = METHOD_GET_COMMON_TIME + 1;
    395     private static final int METHOD_GET_LOCAL_TIME = METHOD_GET_COMMON_FREQ + 1;
    396     private static final int METHOD_GET_LOCAL_FREQ = METHOD_GET_LOCAL_TIME + 1;
    397     private static final int METHOD_GET_ESTIMATED_ERROR = METHOD_GET_LOCAL_FREQ + 1;
    398     private static final int METHOD_GET_TIMELINE_ID = METHOD_GET_ESTIMATED_ERROR + 1;
    399     private static final int METHOD_GET_STATE = METHOD_GET_TIMELINE_ID + 1;
    400     private static final int METHOD_GET_MASTER_ADDRESS = METHOD_GET_STATE + 1;
    401     private static final int METHOD_REGISTER_LISTENER = METHOD_GET_MASTER_ADDRESS + 1;
    402     private static final int METHOD_UNREGISTER_LISTENER = METHOD_REGISTER_LISTENER + 1;
    403 
    404     private static final int METHOD_CBK_ON_TIMELINE_CHANGED = IBinder.FIRST_CALL_TRANSACTION;
    405 }
    406