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