• 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.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      */
CommonClock()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      */
create()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      */
release()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      */
getTime()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      */
getEstimatedError()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      */
getTimelineId()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      */
getState()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      */
getMasterAddr()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          */
onTimelineChanged(long newTimelineId)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      */
setTimelineChangedListener(OnTimelineChangedListener listener)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          */
onServerDied()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      */
setServerDiedListener(OnServerDiedListener listener)293     public void setServerDiedListener(OnServerDiedListener listener) {
294         synchronized (mListenerLock) {
295             mServerDiedListener = listener;
296         }
297     }
298 
finalize()299     protected void finalize() throws Throwable { release(); }
300 
throwOnDeadServer()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
onTransact(int code, Parcel data, Parcel reply, int flags)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 
registerTimelineChangeListener()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 
unregisterTimelineChangeListener()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