blob: 3a1da9761b6bb8e415acae8812ec45afe268e5a5 [file] [log] [blame]
John Grossman37237832012-01-12 11:05:37 -08001/*
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;
23import static libcore.io.OsConstants.*;
24
25import android.content.ComponentName;
26import android.content.Context;
27import android.content.Intent;
28import android.content.ServiceConnection;
29import android.os.Binder;
30import android.os.CommonTimeUtils;
31import android.os.IBinder;
32import android.os.Parcel;
33import android.os.RemoteException;
34import 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 */
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}