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.InetSocketAddress; 19import java.util.NoSuchElementException; 20import android.os.Binder; 21import android.os.CommonTimeUtils; 22import android.os.IBinder; 23import android.os.Parcel; 24import android.os.RemoteException; 25import android.os.ServiceManager; 26import static android.system.OsConstants.*; 27 28/** 29 * Used for accessing the android common time service's common clock and receiving notifications 30 * about common time synchronization status changes. 31 * @hide 32 */ 33public class CommonClock { 34 /** 35 * Sentinel value returned by {@link #getTime()} and {@link #getEstimatedError()} when the 36 * common time service is not able to determine the current common time due to a lack of 37 * synchronization. 38 */ 39 public static final long TIME_NOT_SYNCED = -1; 40 41 /** 42 * Sentinel value returned by {@link #getTimelineId()} when the common time service is not 43 * currently synced to any timeline. 44 */ 45 public static final long INVALID_TIMELINE_ID = 0; 46 47 /** 48 * Sentinel value returned by {@link #getEstimatedError()} when the common time service is not 49 * currently synced to any timeline. 50 */ 51 public static final int ERROR_ESTIMATE_UNKNOWN = 0x7FFFFFFF; 52 53 /** 54 * Value used by {@link #getState()} to indicate that there was an internal error while 55 * attempting to determine the state of the common time service. 56 */ 57 public static final int STATE_INVALID = -1; 58 59 /** 60 * Value used by {@link #getState()} to indicate that the common time service is in its initial 61 * state and attempting to find the current timeline master, if any. The service will 62 * transition to either {@link #STATE_CLIENT} if it finds an active master, or to 63 * {@link #STATE_MASTER} if no active master is found and this client becomes the master of a 64 * new timeline. 65 */ 66 public static final int STATE_INITIAL = 0; 67 68 /** 69 * Value used by {@link #getState()} to indicate that the common time service is in its client 70 * state and is synchronizing its time to a different timeline master on the network. 71 */ 72 public static final int STATE_CLIENT = 1; 73 74 /** 75 * Value used by {@link #getState()} to indicate that the common time service is in its master 76 * state and is serving as the timeline master for other common time service clients on the 77 * network. 78 */ 79 public static final int STATE_MASTER = 2; 80 81 /** 82 * Value used by {@link #getState()} to indicate that the common time service is in its Ronin 83 * state. Common time service instances in the client state enter the Ronin state after their 84 * timeline master becomes unreachable on the network. Common time services who enter the Ronin 85 * state will begin a new master election for the timeline they were recently clients of. As 86 * clients detect they are not the winner and drop out of the election, they will transition to 87 * the {@link #STATE_WAIT_FOR_ELECTION} state. When there is only one client remaining in the 88 * election, it will assume ownership of the timeline and transition to the 89 * {@link #STATE_MASTER} state. During the election, all clients will allow their timeline to 90 * drift without applying correction. 91 */ 92 public static final int STATE_RONIN = 3; 93 94 /** 95 * Value used by {@link #getState()} to indicate that the common time service is waiting for a 96 * master election to conclude and for the new master to announce itself before transitioning to 97 * the {@link #STATE_CLIENT} state. If no new master announces itself within the timeout 98 * threshold, the time service will transition back to the {@link #STATE_RONIN} state in order 99 * to restart the election. 100 */ 101 public static final int STATE_WAIT_FOR_ELECTION = 4; 102 103 /** 104 * Name of the underlying native binder service 105 */ 106 public static final String SERVICE_NAME = "common_time.clock"; 107 108 /** 109 * Class constructor. 110 * @throws android.os.RemoteException 111 */ 112 public CommonClock() 113 throws RemoteException { 114 mRemote = ServiceManager.getService(SERVICE_NAME); 115 if (null == mRemote) 116 throw new RemoteException(); 117 118 mInterfaceDesc = mRemote.getInterfaceDescriptor(); 119 mUtils = new CommonTimeUtils(mRemote, mInterfaceDesc); 120 mRemote.linkToDeath(mDeathHandler, 0); 121 registerTimelineChangeListener(); 122 } 123 124 /** 125 * Handy class factory method. 126 */ 127 static public CommonClock create() { 128 CommonClock retVal; 129 130 try { 131 retVal = new CommonClock(); 132 } 133 catch (RemoteException e) { 134 retVal = null; 135 } 136 137 return retVal; 138 } 139 140 /** 141 * Release all native resources held by this {@link android.os.CommonClock} instance. Once 142 * resources have been released, the {@link android.os.CommonClock} instance is disconnected from 143 * the native service and will throw a {@link android.os.RemoteException} if any of its 144 * methods are called. Clients should always call release on their client instances before 145 * releasing their last Java reference to the instance. Failure to do this will cause 146 * non-deterministic native resource reclamation and may cause the common time service to remain 147 * active on the network for longer than it should. 148 */ 149 public void release() { 150 unregisterTimelineChangeListener(); 151 if (null != mRemote) { 152 try { 153 mRemote.unlinkToDeath(mDeathHandler, 0); 154 } 155 catch (NoSuchElementException e) { } 156 mRemote = null; 157 } 158 mUtils = null; 159 } 160 161 /** 162 * Gets the common clock's current time. 163 * 164 * @return a signed 64-bit value representing the current common time in microseconds, or the 165 * special value {@link #TIME_NOT_SYNCED} if the common time service is currently not 166 * synchronized. 167 * @throws android.os.RemoteException 168 */ 169 public long getTime() 170 throws RemoteException { 171 throwOnDeadServer(); 172 return mUtils.transactGetLong(METHOD_GET_COMMON_TIME, TIME_NOT_SYNCED); 173 } 174 175 /** 176 * Gets the current estimation of common clock's synchronization accuracy from the common time 177 * service. 178 * 179 * @return a signed 32-bit value representing the common time service's estimation of 180 * synchronization accuracy in microseconds, or the special value 181 * {@link #ERROR_ESTIMATE_UNKNOWN} if the common time service is currently not synchronized. 182 * Negative values indicate that the local server estimates that the nominal common time is 183 * behind the local server's time (in other words, the local clock is running fast) Positive 184 * values indicate that the local server estimates that the nominal common time is ahead of the 185 * local server's time (in other words, the local clock is running slow) 186 * @throws android.os.RemoteException 187 */ 188 public int getEstimatedError() 189 throws RemoteException { 190 throwOnDeadServer(); 191 return mUtils.transactGetInt(METHOD_GET_ESTIMATED_ERROR, ERROR_ESTIMATE_UNKNOWN); 192 } 193 194 /** 195 * Gets the ID of the timeline the common time service is currently synchronizing its clock to. 196 * 197 * @return a long representing the unique ID of the timeline the common time service is 198 * currently synchronizing with, or {@link #INVALID_TIMELINE_ID} if the common time service is 199 * currently not synchronized. 200 * @throws android.os.RemoteException 201 */ 202 public long getTimelineId() 203 throws RemoteException { 204 throwOnDeadServer(); 205 return mUtils.transactGetLong(METHOD_GET_TIMELINE_ID, INVALID_TIMELINE_ID); 206 } 207 208 /** 209 * Gets the current state of this clock's common time service in the the master election 210 * algorithm. 211 * 212 * @return a integer indicating the current state of the this clock's common time service in the 213 * master election algorithm or {@link #STATE_INVALID} if there is an internal error. 214 * @throws android.os.RemoteException 215 */ 216 public int getState() 217 throws RemoteException { 218 throwOnDeadServer(); 219 return mUtils.transactGetInt(METHOD_GET_STATE, STATE_INVALID); 220 } 221 222 /** 223 * Gets the IP address and UDP port of the current timeline master. 224 * 225 * @return an InetSocketAddress containing the IP address and UDP port of the current timeline 226 * master, or null if there is no current master. 227 * @throws android.os.RemoteException 228 */ 229 public InetSocketAddress getMasterAddr() 230 throws RemoteException { 231 throwOnDeadServer(); 232 return mUtils.transactGetSockaddr(METHOD_GET_MASTER_ADDRESS); 233 } 234 235 /** 236 * The OnTimelineChangedListener interface defines a method called by the 237 * {@link android.os.CommonClock} instance to indicate that the time synchronization service has 238 * either synchronized with a new timeline, or is no longer a member of any timeline. The 239 * client application can implement this interface and register the listener with the 240 * {@link #setTimelineChangedListener(OnTimelineChangedListener)} method. 241 */ 242 public interface OnTimelineChangedListener { 243 /** 244 * Method called when the time service's timeline has changed. 245 * 246 * @param newTimelineId a long which uniquely identifies the timeline the time 247 * synchronization service is now a member of, or {@link #INVALID_TIMELINE_ID} if the the 248 * service is not synchronized to any timeline. 249 */ 250 void onTimelineChanged(long newTimelineId); 251 } 252 253 /** 254 * Registers an OnTimelineChangedListener interface. 255 * <p>Call this method with a null listener to stop receiving server death notifications. 256 */ 257 public void setTimelineChangedListener(OnTimelineChangedListener listener) { 258 synchronized (mListenerLock) { 259 mTimelineChangedListener = listener; 260 } 261 } 262 263 /** 264 * The OnServerDiedListener interface defines a method called by the 265 * {@link android.os.CommonClock} instance to indicate that the connection to the native media 266 * server has been broken and that the {@link android.os.CommonClock} instance will need to be 267 * released and re-created. The client application can implement this interface and register 268 * the listener with the {@link #setServerDiedListener(OnServerDiedListener)} method. 269 */ 270 public interface OnServerDiedListener { 271 /** 272 * Method called when the native media server has died. <p>If the native common time 273 * service encounters a fatal error and needs to restart, the binder connection from the 274 * {@link android.os.CommonClock} instance to the common time service will be broken. To 275 * restore functionality, clients should {@link #release()} their old visualizer and create 276 * a new instance. 277 */ 278 void onServerDied(); 279 } 280 281 /** 282 * Registers an OnServerDiedListener interface. 283 * <p>Call this method with a null listener to stop receiving server death notifications. 284 */ 285 public void setServerDiedListener(OnServerDiedListener listener) { 286 synchronized (mListenerLock) { 287 mServerDiedListener = listener; 288 } 289 } 290 291 protected void finalize() throws Throwable { release(); } 292 293 private void throwOnDeadServer() throws RemoteException { 294 if ((null == mRemote) || (null == mUtils)) 295 throw new RemoteException(); 296 } 297 298 private final Object mListenerLock = new Object(); 299 private OnTimelineChangedListener mTimelineChangedListener = null; 300 private OnServerDiedListener mServerDiedListener = null; 301 302 private IBinder mRemote = null; 303 private String mInterfaceDesc = ""; 304 private CommonTimeUtils mUtils; 305 306 private IBinder.DeathRecipient mDeathHandler = new IBinder.DeathRecipient() { 307 public void binderDied() { 308 synchronized (mListenerLock) { 309 if (null != mServerDiedListener) 310 mServerDiedListener.onServerDied(); 311 } 312 } 313 }; 314 315 private class TimelineChangedListener extends Binder { 316 @Override 317 protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) 318 throws RemoteException { 319 switch (code) { 320 case METHOD_CBK_ON_TIMELINE_CHANGED: 321 data.enforceInterface(DESCRIPTOR); 322 long timelineId = data.readLong(); 323 synchronized (mListenerLock) { 324 if (null != mTimelineChangedListener) 325 mTimelineChangedListener.onTimelineChanged(timelineId); 326 } 327 return true; 328 } 329 330 return super.onTransact(code, data, reply, flags); 331 } 332 333 private static final String DESCRIPTOR = "android.os.ICommonClockListener"; 334 }; 335 336 private TimelineChangedListener mCallbackTgt = null; 337 338 private void registerTimelineChangeListener() throws RemoteException { 339 if (null != mCallbackTgt) 340 return; 341 342 boolean success = false; 343 android.os.Parcel data = android.os.Parcel.obtain(); 344 android.os.Parcel reply = android.os.Parcel.obtain(); 345 mCallbackTgt = new TimelineChangedListener(); 346 347 try { 348 data.writeInterfaceToken(mInterfaceDesc); 349 data.writeStrongBinder(mCallbackTgt); 350 mRemote.transact(METHOD_REGISTER_LISTENER, data, reply, 0); 351 success = (0 == reply.readInt()); 352 } 353 catch (RemoteException e) { 354 success = false; 355 } 356 finally { 357 reply.recycle(); 358 data.recycle(); 359 } 360 361 // Did we catch a remote exception or fail to register our callback target? If so, our 362 // object must already be dead (or be as good as dead). Clear out all of our state so that 363 // our other methods will properly indicate a dead object. 364 if (!success) { 365 mCallbackTgt = null; 366 mRemote = null; 367 mUtils = null; 368 } 369 } 370 371 private void unregisterTimelineChangeListener() { 372 if (null == mCallbackTgt) 373 return; 374 375 android.os.Parcel data = android.os.Parcel.obtain(); 376 android.os.Parcel reply = android.os.Parcel.obtain(); 377 378 try { 379 data.writeInterfaceToken(mInterfaceDesc); 380 data.writeStrongBinder(mCallbackTgt); 381 mRemote.transact(METHOD_UNREGISTER_LISTENER, data, reply, 0); 382 } 383 catch (RemoteException e) { } 384 finally { 385 reply.recycle(); 386 data.recycle(); 387 mCallbackTgt = null; 388 } 389 } 390 391 private static final int METHOD_IS_COMMON_TIME_VALID = IBinder.FIRST_CALL_TRANSACTION; 392 private static final int METHOD_COMMON_TIME_TO_LOCAL_TIME = METHOD_IS_COMMON_TIME_VALID + 1; 393 private static final int METHOD_LOCAL_TIME_TO_COMMON_TIME = METHOD_COMMON_TIME_TO_LOCAL_TIME + 1; 394 private static final int METHOD_GET_COMMON_TIME = METHOD_LOCAL_TIME_TO_COMMON_TIME + 1; 395 private static final int METHOD_GET_COMMON_FREQ = METHOD_GET_COMMON_TIME + 1; 396 private static final int METHOD_GET_LOCAL_TIME = METHOD_GET_COMMON_FREQ + 1; 397 private static final int METHOD_GET_LOCAL_FREQ = METHOD_GET_LOCAL_TIME + 1; 398 private static final int METHOD_GET_ESTIMATED_ERROR = METHOD_GET_LOCAL_FREQ + 1; 399 private static final int METHOD_GET_TIMELINE_ID = METHOD_GET_ESTIMATED_ERROR + 1; 400 private static final int METHOD_GET_STATE = METHOD_GET_TIMELINE_ID + 1; 401 private static final int METHOD_GET_MASTER_ADDRESS = METHOD_GET_STATE + 1; 402 private static final int METHOD_REGISTER_LISTENER = METHOD_GET_MASTER_ADDRESS + 1; 403 private static final int METHOD_UNREGISTER_LISTENER = METHOD_REGISTER_LISTENER + 1; 404 405 private static final int METHOD_CBK_ON_TIMELINE_CHANGED = IBinder.FIRST_CALL_TRANSACTION; 406} 407