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 */ 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