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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 284 public void setServerDiedListener(OnServerDiedListener listener) { 285 synchronized (mListenerLock) { 286 mServerDiedListener = listener; 287 } 288 } 289 290 protected void finalize() throws Throwable { release(); } 291 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 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 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 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