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