• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
CommonClock()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      */
create()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      */
release()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      */
getTime()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      */
getEstimatedError()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      */
getTimelineId()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      */
getState()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      */
getMasterAddr()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          */
onTimelineChanged(long newTimelineId)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      */
setTimelineChangedListener(OnTimelineChangedListener listener)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          */
onServerDied()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      */
setServerDiedListener(OnServerDiedListener listener)284     public void setServerDiedListener(OnServerDiedListener listener) {
285         synchronized (mListenerLock) {
286             mServerDiedListener = listener;
287         }
288     }
289 
finalize()290     protected void finalize() throws Throwable { release(); }
291 
throwOnDeadServer()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
onTransact(int code, Parcel data, Parcel reply, int flags)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 
registerTimelineChangeListener()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 
unregisterTimelineChangeListener()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