CommonClock.java revision 34385d352da19805ae948215e2edbeedd16b7941
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 */
16package android.os;
17
18import java.net.InetAddress;
19import java.net.Inet4Address;
20import java.net.Inet6Address;
21import java.net.InetSocketAddress;
22import java.util.NoSuchElementException;
23
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.ServiceConnection;
28import android.os.Binder;
29import android.os.CommonTimeUtils;
30import android.os.IBinder;
31import android.os.Parcel;
32import android.os.RemoteException;
33import android.os.ServiceManager;
34import static android.system.OsConstants.*;
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 */
41public 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