summaryrefslogtreecommitdiffstats
path: root/core/java/android/os/CommonClock.java
blob: e728144bc91a29a629a2ba46a27320fb4943ee0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package android.os;

import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetSocketAddress;
import java.util.NoSuchElementException;
import static libcore.io.OsConstants.*;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;

class CommonTimeUtils {
    /**
     * Successful operation.
     */
    public static final int SUCCESS = 0;
    /**
     * Unspecified error.
     */
    public static final int ERROR = -1;
    /**
     * Operation failed due to bad parameter value.
     */
    public static final int ERROR_BAD_VALUE = -4;
    /**
     * Operation failed due to dead remote object.
     */
    public static final int ERROR_DEAD_OBJECT = -7;

    public CommonTimeUtils(IBinder remote, String interfaceDesc) {
        mRemote = remote;
        mInterfaceDesc = interfaceDesc;
    }

    public int transactGetInt(int method_code, int error_ret_val)
    throws RemoteException {
        android.os.Parcel data  = android.os.Parcel.obtain();
        android.os.Parcel reply = android.os.Parcel.obtain();
        int ret_val;

        try {
            int res;
            data.writeInterfaceToken(mInterfaceDesc);
            mRemote.transact(method_code, data, reply, 0);

            res = reply.readInt();
            ret_val = (0 == res) ? reply.readInt() : error_ret_val;
        }
        finally {
            reply.recycle();
            data.recycle();
        }

        return ret_val;
    }

    public int transactSetInt(int method_code, int val) {
        android.os.Parcel data  = android.os.Parcel.obtain();
        android.os.Parcel reply = android.os.Parcel.obtain();

        try {
            data.writeInterfaceToken(mInterfaceDesc);
            data.writeInt(val);
            mRemote.transact(method_code, data, reply, 0);

            return reply.readInt();
        }
        catch (RemoteException e) {
            return ERROR_DEAD_OBJECT;
        }
        finally {
            reply.recycle();
            data.recycle();
        }
    }

    public long transactGetLong(int method_code, long error_ret_val)
    throws RemoteException {
        android.os.Parcel data  = android.os.Parcel.obtain();
        android.os.Parcel reply = android.os.Parcel.obtain();
        long ret_val;

        try {
            int res;
            data.writeInterfaceToken(mInterfaceDesc);
            mRemote.transact(method_code, data, reply, 0);

            res = reply.readInt();
            ret_val = (0 == res) ? reply.readLong() : error_ret_val;
        }
        finally {
            reply.recycle();
            data.recycle();
        }

        return ret_val;
    }

    public int transactSetLong(int method_code, long val) {
        android.os.Parcel data  = android.os.Parcel.obtain();
        android.os.Parcel reply = android.os.Parcel.obtain();

        try {
            data.writeInterfaceToken(mInterfaceDesc);
            data.writeLong(val);
            mRemote.transact(method_code, data, reply, 0);

            return reply.readInt();
        }
        catch (RemoteException e) {
            return ERROR_DEAD_OBJECT;
        }
        finally {
            reply.recycle();
            data.recycle();
        }
    }

    public String transactGetString(int method_code, String error_ret_val)
    throws RemoteException {
        android.os.Parcel data  = android.os.Parcel.obtain();
        android.os.Parcel reply = android.os.Parcel.obtain();
        String ret_val;

        try {
            int res;
            data.writeInterfaceToken(mInterfaceDesc);
            mRemote.transact(method_code, data, reply, 0);

            res = reply.readInt();
            ret_val = (0 == res) ? reply.readString() : error_ret_val;
        }
        finally {
            reply.recycle();
            data.recycle();
        }

        return ret_val;
    }

    public int transactSetString(int method_code, String val) {
        android.os.Parcel data  = android.os.Parcel.obtain();
        android.os.Parcel reply = android.os.Parcel.obtain();

        try {
            data.writeInterfaceToken(mInterfaceDesc);
            data.writeString(val);
            mRemote.transact(method_code, data, reply, 0);

            return reply.readInt();
        }
        catch (RemoteException e) {
            return ERROR_DEAD_OBJECT;
        }
        finally {
            reply.recycle();
            data.recycle();
        }
    }

    public InetSocketAddress transactGetSockaddr(int method_code)
    throws RemoteException {
        android.os.Parcel data  = android.os.Parcel.obtain();
        android.os.Parcel reply = android.os.Parcel.obtain();
        InetSocketAddress ret_val = null;

        try {
            int res;
            data.writeInterfaceToken(mInterfaceDesc);
            mRemote.transact(method_code, data, reply, 0);

            res = reply.readInt();
            if (0 == res) {
                int type;
                int port = 0;
                String addrStr = null;

                type = reply.readInt();

                if (AF_INET == type) {
                    int addr = reply.readInt();
                    port = reply.readInt();
                    addrStr = String.format("%d.%d.%d.%d", (addr >> 24) & 0xFF,
                                                           (addr >> 16) & 0xFF,
                                                           (addr >>  8) & 0xFF,
                                                            addr        & 0xFF);
                } else if (AF_INET6 == type) {
                    int addr1 = reply.readInt();
                    int addr2 = reply.readInt();
                    int addr3 = reply.readInt();
                    int addr4 = reply.readInt();

                    port = reply.readInt();

                    int flowinfo = reply.readInt();
                    int scope_id = reply.readInt();

                    addrStr = String.format("[%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X]",
                                            (addr1 >> 16) & 0xFFFF, addr1 & 0xFFFF,
                                            (addr2 >> 16) & 0xFFFF, addr2 & 0xFFFF,
                                            (addr3 >> 16) & 0xFFFF, addr3 & 0xFFFF,
                                            (addr4 >> 16) & 0xFFFF, addr4 & 0xFFFF);
                }

                if (null != addrStr) {
                    ret_val = new InetSocketAddress(addrStr, port);
                }
            }
        }
        finally {
            reply.recycle();
            data.recycle();
        }

        return ret_val;
    }

    public int transactSetSockaddr(int method_code, InetSocketAddress addr) {
        android.os.Parcel data  = android.os.Parcel.obtain();
        android.os.Parcel reply = android.os.Parcel.obtain();
        int ret_val = ERROR;

        try {
            data.writeInterfaceToken(mInterfaceDesc);

            if (null == addr) {
                data.writeInt(0);
            } else {
                data.writeInt(1);
                final InetAddress a = addr.getAddress();
                final byte[]      b = a.getAddress();
                final int         p = addr.getPort();

                if (a instanceof Inet4Address) {
                    int v4addr = (((int)b[0] & 0xFF) << 24) |
                                 (((int)b[1] & 0xFF) << 16) |
                                 (((int)b[2] & 0xFF) << 8) |
                                  ((int)b[3] & 0xFF);

                    data.writeInt(AF_INET);
                    data.writeInt(v4addr);
                    data.writeInt(p);
                } else
                if (a instanceof Inet6Address) {
                    int i;
                    Inet6Address v6 = (Inet6Address)a;
                    data.writeInt(AF_INET6);
                    for (i = 0; i < 4; ++i) {
                        int aword = (((int)b[(i*4) + 0] & 0xFF) << 24) |
                                    (((int)b[(i*4) + 1] & 0xFF) << 16) |
                                    (((int)b[(i*4) + 2] & 0xFF) << 8) |
                                     ((int)b[(i*4) + 3] & 0xFF);
                        data.writeInt(aword);
                    }
                    data.writeInt(p);
                    data.writeInt(0);   // flow info
                    data.writeInt(v6.getScopeId());
                } else {
                    return ERROR_BAD_VALUE;
                }
            }

            mRemote.transact(method_code, data, reply, 0);
            ret_val = reply.readInt();
        }
        catch (RemoteException e) {
            ret_val = ERROR_DEAD_OBJECT;
        }
        finally {
            reply.recycle();
            data.recycle();
        }

        return ret_val;
    }

    private IBinder mRemote;
    private String mInterfaceDesc;
};

/**
 * Used for accessing the android common time service's common clock and receiving notifications
 * about common time synchronization status changes.
 * @hide
 */
public class CommonClock {
    /**
     * Sentinel value returned by {@link #getTime()} and {@link #getEstimatedError()} when the
     * common time service is not able to determine the current common time due to a lack of
     * synchronization.
     */
    public static final long TIME_NOT_SYNCED = -1;

    /**
     * Sentinel value returned by {@link #getTimelineId()} when the common time service is not
     * currently synced to any timeline.
     */
    public static final long INVALID_TIMELINE_ID = 0;

    /**
     * Sentinel value returned by {@link #getEstimatedError()} when the common time service is not
     * currently synced to any timeline.
     */
    public static final int ERROR_ESTIMATE_UNKNOWN = 0x7FFFFFFF;

    /**
     * Value used by {@link #getState()} to indicate that there was an internal error while
     * attempting to determine the state of the common time service.
     */
    public static final int STATE_INVALID = -1;

    /**
     * Value used by {@link #getState()} to indicate that the common time service is in its initial
     * state and attempting to find the current timeline master, if any.  The service will
     * transition to either {@link #STATE_CLIENT} if it finds an active master, or to
     * {@link #STATE_MASTER} if no active master is found and this client becomes the master of a
     * new timeline.
     */
    public static final int STATE_INITIAL = 0;

    /**
     * Value used by {@link #getState()} to indicate that the common time service is in its client
     * state and is synchronizing its time to a different timeline master on the network.
     */
    public static final int STATE_CLIENT = 1;

    /**
     * Value used by {@link #getState()} to indicate that the common time service is in its master
     * state and is serving as the timeline master for other common time service clients on the
     * network.
     */
    public static final int STATE_MASTER = 2;

    /**
     * Value used by {@link #getState()} to indicate that the common time service is in its Ronin
     * state.  Common time service instances in the client state enter the Ronin state after their
     * timeline master becomes unreachable on the network.  Common time services who enter the Ronin
     * state will begin a new master election for the timeline they were recently clients of.  As
     * clients detect they are not the winner and drop out of the election, they will transition to
     * the {@link #STATE_WAIT_FOR_ELECTION} state.  When there is only one client remaining in the
     * election, it will assume ownership of the timeline and transition to the
     * {@link #STATE_MASTER} state.  During the election, all clients will allow their timeline to
     * drift without applying correction.
     */
    public static final int STATE_RONIN = 3;

    /**
     * Value used by {@link #getState()} to indicate that the common time service is waiting for a
     * master election to conclude and for the new master to announce itself before transitioning to
     * the {@link #STATE_CLIENT} state.  If no new master announces itself within the timeout
     * threshold, the time service will transition back to the {@link #STATE_RONIN} state in order
     * to restart the election.
     */
    public static final int STATE_WAIT_FOR_ELECTION = 4;

    /**
     * Name of the underlying native binder service
     */
    public static final String SERVICE_NAME = "common_time.clock";

    /**
     * Class constructor.
     * @throws android.os.RemoteException
     */
    public CommonClock()
    throws RemoteException {
        mRemote = ServiceManager.getService(SERVICE_NAME);
        if (null == mRemote)
            throw new RemoteException();

        mInterfaceDesc = mRemote.getInterfaceDescriptor();
        mUtils = new CommonTimeUtils(mRemote, mInterfaceDesc);
        mRemote.linkToDeath(mDeathHandler, 0);
        registerTimelineChangeListener();
    }

    /**
     * Handy class factory method.
     */
    static public CommonClock create() {
        CommonClock retVal;

        try {
            retVal = new CommonClock();
        }
        catch (RemoteException e) {
            retVal = null;
        }

        return retVal;
    }

    /**
     * Release all native resources held by this {@link android.os.CommonClock} instance.  Once
     * resources have been released, the {@link android.os.CommonClock} instance is disconnected from
     * the native service and will throw a {@link android.os.RemoteException} if any of its
     * methods are called.  Clients should always call release on their client instances before
     * releasing their last Java reference to the instance.  Failure to do this will cause
     * non-deterministic native resource reclamation and may cause the common time service to remain
     * active on the network for longer than it should.
     */
    public void release() {
        unregisterTimelineChangeListener();
        if (null != mRemote) {
            try {
                mRemote.unlinkToDeath(mDeathHandler, 0);
            }
            catch (NoSuchElementException e) { }
            mRemote = null;
        }
        mUtils = null;
    }

    /**
     * Gets the common clock's current time.
     *
     * @return a signed 64-bit value representing the current common time in microseconds, or the
     * special value {@link #TIME_NOT_SYNCED} if the common time service is currently not
     * synchronized.
     * @throws android.os.RemoteException
     */
    public long getTime()
    throws RemoteException {
        throwOnDeadServer();
        return mUtils.transactGetLong(METHOD_GET_COMMON_TIME, TIME_NOT_SYNCED);
    }

    /**
     * Gets the current estimation of common clock's synchronization accuracy from the common time
     * service.
     *
     * @return a signed 32-bit value representing the common time service's estimation of
     * synchronization accuracy in microseconds, or the special value
     * {@link #ERROR_ESTIMATE_UNKNOWN} if the common time service is currently not synchronized.
     * Negative values indicate that the local server estimates that the nominal common time is
     * behind the local server's time (in other words, the local clock is running fast) Positive
     * values indicate that the local server estimates that the nominal common time is ahead of the
     * local server's time (in other words, the local clock is running slow)
     * @throws android.os.RemoteException
     */
    public int getEstimatedError()
    throws RemoteException {
        throwOnDeadServer();
        return mUtils.transactGetInt(METHOD_GET_ESTIMATED_ERROR, ERROR_ESTIMATE_UNKNOWN);
    }

    /**
     * Gets the ID of the timeline the common time service is currently synchronizing its clock to.
     *
     * @return a long representing the unique ID of the timeline the common time service is
     * currently synchronizing with, or {@link #INVALID_TIMELINE_ID} if the common time service is
     * currently not synchronized.
     * @throws android.os.RemoteException
     */
    public long getTimelineId()
    throws RemoteException {
        throwOnDeadServer();
        return mUtils.transactGetLong(METHOD_GET_TIMELINE_ID, INVALID_TIMELINE_ID);
    }

    /**
     * Gets the current state of this clock's common time service in the the master election
     * algorithm.
     *
     * @return a integer indicating the current state of the this clock's common time service in the
     * master election algorithm or {@link #STATE_INVALID} if there is an internal error.
     * @throws android.os.RemoteException
     */
    public int getState()
    throws RemoteException {
        throwOnDeadServer();
        return mUtils.transactGetInt(METHOD_GET_STATE, STATE_INVALID);
    }

    /**
     * Gets the IP address and UDP port of the current timeline master.
     *
     * @return an InetSocketAddress containing the IP address and UDP port of the current timeline
     * master, or null if there is no current master.
     * @throws android.os.RemoteException
     */
    public InetSocketAddress getMasterAddr()
    throws RemoteException {
        throwOnDeadServer();
        return mUtils.transactGetSockaddr(METHOD_GET_MASTER_ADDRESS);
    }

    /**
     * The OnTimelineChangedListener interface defines a method called by the
     * {@link android.os.CommonClock} instance to indicate that the time synchronization service has
     * either synchronized with a new timeline, or is no longer a member of any timeline.  The
     * client application can implement this interface and register the listener with the
     * {@link #setTimelineChangedListener(OnTimelineChangedListener)} method.
     */
    public interface OnTimelineChangedListener  {
        /**
         * Method called when the time service's timeline has changed.
         *
         * @param newTimelineId a long which uniquely identifies the timeline the time
         * synchronization service is now a member of, or {@link #INVALID_TIMELINE_ID} if the the
         * service is not synchronized to any timeline.
         */
        void onTimelineChanged(long newTimelineId);
    }

    /**
     * Registers an OnTimelineChangedListener interface.
     * <p>Call this method with a null listener to stop receiving server death notifications.
     */
    public void setTimelineChangedListener(OnTimelineChangedListener listener) {
        synchronized (mListenerLock) {
            mTimelineChangedListener = listener;
        }
    }

    /**
     * The OnServerDiedListener interface defines a method called by the
     * {@link android.os.CommonClock} instance to indicate that the connection to the native media
     * server has been broken and that the {@link android.os.CommonClock} instance will need to be
     * released and re-created.  The client application can implement this interface and register
     * the listener with the {@link #setServerDiedListener(OnServerDiedListener)} method.
     */
    public interface OnServerDiedListener  {
        /**
         * Method called when the native media server has died.  <p>If the native common time
         * service encounters a fatal error and needs to restart, the binder connection from the
         * {@link android.os.CommonClock} instance to the common time service will be broken.  To
         * restore functionality, clients should {@link #release()} their old visualizer and create
         * a new instance.
         */
        void onServerDied();
    }

    /**
     * Registers an OnServerDiedListener interface.
     * <p>Call this method with a null listener to stop receiving server death notifications.
     */
    public void setServerDiedListener(OnServerDiedListener listener) {
        synchronized (mListenerLock) {
            mServerDiedListener = listener;
        }
    }

    protected void finalize() throws Throwable { release(); }

    private void throwOnDeadServer() throws RemoteException {
        if ((null == mRemote) || (null == mUtils))
            throw new RemoteException();
    }

    private final Object mListenerLock = new Object();
    private OnTimelineChangedListener mTimelineChangedListener = null;
    private OnServerDiedListener mServerDiedListener = null;

    private IBinder mRemote = null;
    private String mInterfaceDesc = "";
    private CommonTimeUtils mUtils;

    private IBinder.DeathRecipient mDeathHandler = new IBinder.DeathRecipient() {
        public void binderDied() {
            synchronized (mListenerLock) {
                if (null != mServerDiedListener)
                    mServerDiedListener.onServerDied();
            }
        }
    };

    private class TimelineChangedListener extends Binder {
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException {
            switch (code) {
                case METHOD_CBK_ON_TIMELINE_CHANGED:
                    data.enforceInterface(DESCRIPTOR);
                    long timelineId = data.readLong();
                    synchronized (mListenerLock) {
                        if (null != mTimelineChangedListener)
                            mTimelineChangedListener.onTimelineChanged(timelineId);
                    }
                    return true;
            }

            return super.onTransact(code, data, reply, flags);
        }

        private static final String DESCRIPTOR = "android.os.ICommonClockListener";
    };

    private TimelineChangedListener mCallbackTgt = null;

    private void registerTimelineChangeListener() throws RemoteException {
        if (null != mCallbackTgt)
            return;

        boolean success = false;
        android.os.Parcel data  = android.os.Parcel.obtain();
        android.os.Parcel reply = android.os.Parcel.obtain();
        mCallbackTgt = new TimelineChangedListener();

        try {
            data.writeInterfaceToken(mInterfaceDesc);
            data.writeStrongBinder(mCallbackTgt);
            mRemote.transact(METHOD_REGISTER_LISTENER, data, reply, 0);
            success = (0 == reply.readInt());
        }
        catch (RemoteException e) {
            success = false;
        }
        finally {
            reply.recycle();
            data.recycle();
        }

        // Did we catch a remote exception or fail to register our callback target?  If so, our
        // object must already be dead (or be as good as dead).  Clear out all of our state so that
        // our other methods will properly indicate a dead object.
        if (!success) {
            mCallbackTgt = null;
            mRemote = null;
            mUtils = null;
        }
    }

    private void unregisterTimelineChangeListener() {
        if (null == mCallbackTgt)
            return;

        android.os.Parcel data  = android.os.Parcel.obtain();
        android.os.Parcel reply = android.os.Parcel.obtain();

        try {
            data.writeInterfaceToken(mInterfaceDesc);
            data.writeStrongBinder(mCallbackTgt);
            mRemote.transact(METHOD_UNREGISTER_LISTENER, data, reply, 0);
        }
        catch (RemoteException e) { }
        finally {
            reply.recycle();
            data.recycle();
            mCallbackTgt = null;
        }
    }

    private static final int METHOD_IS_COMMON_TIME_VALID = IBinder.FIRST_CALL_TRANSACTION;
    private static final int METHOD_COMMON_TIME_TO_LOCAL_TIME = METHOD_IS_COMMON_TIME_VALID + 1;
    private static final int METHOD_LOCAL_TIME_TO_COMMON_TIME = METHOD_COMMON_TIME_TO_LOCAL_TIME + 1;
    private static final int METHOD_GET_COMMON_TIME = METHOD_LOCAL_TIME_TO_COMMON_TIME + 1;
    private static final int METHOD_GET_COMMON_FREQ = METHOD_GET_COMMON_TIME + 1;
    private static final int METHOD_GET_LOCAL_TIME = METHOD_GET_COMMON_FREQ + 1;
    private static final int METHOD_GET_LOCAL_FREQ = METHOD_GET_LOCAL_TIME + 1;
    private static final int METHOD_GET_ESTIMATED_ERROR = METHOD_GET_LOCAL_FREQ + 1;
    private static final int METHOD_GET_TIMELINE_ID = METHOD_GET_ESTIMATED_ERROR + 1;
    private static final int METHOD_GET_STATE = METHOD_GET_TIMELINE_ID + 1;
    private static final int METHOD_GET_MASTER_ADDRESS = METHOD_GET_STATE + 1;
    private static final int METHOD_REGISTER_LISTENER = METHOD_GET_MASTER_ADDRESS + 1;
    private static final int METHOD_UNREGISTER_LISTENER = METHOD_REGISTER_LISTENER + 1;

    private static final int METHOD_CBK_ON_TIMELINE_CHANGED = IBinder.FIRST_CALL_TRANSACTION;
}