Purvil Bambharolia
Purvil Bambharolia

Reputation: 681

How can I sync the system clocks of two or more android phones?

I am trying to develop an app which allows other android phones to act as speakers and thus creating a party like thing. The app would be similar to the group play feature provided by Samsung.

To implement this feature, I decided to perform the following steps -

  1. The host creates a hotspot and publishes a party name on the hotspot and waits for connections.
  2. The client can now see the available party names in a listview and selects a party to connect.
  3. Once the connection between the host and the client is established, the host is directed to an activity where a list of songs appears. The host selects one of the songs.
  4. The selected song is transferred to the client using socket programming and saved in a folder.
  5. After that, the host gets the current system time and adds 5 seconds to it and sends this seconds to the client. For ex - 10:35 - current time. Hmm, we will play at 10:40
  6. Once the host is done sending the time and client has received the time, both of them create an alarm which triggers them to start playing the MP3 file using a mediaplayer.

**Problems faced - ** After I implemented this, I noticed that both the Android devices had different system time, hence I synced the system times using the ClockSync App which uses NTP for synchronising the time. I do not want my users to use a third party app which requires root access. So how can I sync the clocks of two android phones? How can I solve the problem?

EDIT - I am using below AsyncTask Class to calculate the difference between NTP clock and local Clock.

public class offSetAsyncTask extends AsyncTask<Void,Void,Double> {

private String serverName;
private double localClockOffset;
private double destinationTimestamp;
private double roundTripDelay;
double total = 0;
Context context;
double avg;

@Override
protected Double doInBackground(Void... params) {


    getAllForMe();
    getAllForMe();
    getAllForMe();
    getAllForMe();
    getAllForMe();

    System.out.println("!!!!!!!" + total);
    avg = total/5;
    System.out.println("~~~avg. Lag: " +  avg);

    response.processFinish(avg);

    return avg;
}

public interface AsyncResponse{
    void processFinish(double offSet);
}

public AsyncResponse response = null;


public offSetAsyncTask(AsyncResponse res, String name, Context c){
    response = res;
    serverName = name;
    context = c;
}

private void getAllForMe(){

    try{
        DatagramSocket socket = new DatagramSocket();
        InetAddress address = InetAddress.getByName(serverName);
        byte[] buf = new NtpMessage().toByteArray();
        DatagramPacket packet =
                new DatagramPacket(buf, buf.length, address, 123);

        // Set the transmit timestamp *just* before sending the packet
        // ToDo: Does this actually improve performance or not?
        NtpMessage.encodeTimestamp(packet.getData(), 40,
                (System.currentTimeMillis()/1000.0) + 2208988800.0);

        socket.send(packet);


        // Get response
        System.out.println("NTP request sent, waiting for response...\n");
        packet = new DatagramPacket(buf, buf.length);
        socket.receive(packet);

        // Immediately record the incoming timestamp
        destinationTimestamp =
                (System.currentTimeMillis()/1000.0) + 2208988800.0;


        // Process response
        NtpMessage msg = new NtpMessage(packet.getData());

        // Corrected, according to RFC2030 errata
        roundTripDelay = (destinationTimestamp-msg.originateTimestamp) -
                (msg.transmitTimestamp-msg.receiveTimestamp);

        localClockOffset =
                ((msg.receiveTimestamp - msg.originateTimestamp) +
                        (msg.transmitTimestamp - destinationTimestamp)) / 2;

        total+=localClockOffset;

        // Display response
        System.out.println("NTP server: " + serverName);
        System.out.println(msg.toString());

        System.out.println("Dest. timestamp:     " +
                NtpMessage.timestampToString(destinationTimestamp));

        System.out.println("Round-trip delay: " +
                new DecimalFormat("0.00").format(roundTripDelay*1000) + " ms");

        System.out.println("Local clock offset: " +
                new DecimalFormat("0.00").format(localClockOffset*1000) + " ms");

        socket.close();
    } catch (Exception e){
        e.printStackTrace();
    }
    }

}

Upvotes: 6

Views: 3082

Answers (2)

Nick Cardoso
Nick Cardoso

Reputation: 21783

If it's acceptable I think we should probably switch the perspective - instead of syncing devices with each other aiming for A == B (and having to manually account for offsets and delays), we can have each device sync with an external source (server) since if A == C and B == C then A == B.

Your use case will look something like:

  1. The host creates a hotspot and publishes (...)
  2. The client can now see the available party (...)
  3. Once the connection between the host and the client is established (...)
  4. The selected song is transferred to the client (...)
  5. Each Client/Host contacts the server to get the 'correct' time
  6. The offset between the received time and device time is stored
  7. Client broadcasts to Host that time is aligned
  8. When all clients have marked time aligned the host broadcasts the required start time
  9. When clients receive requested start time they use the offset they stored to calculate the start time according to their devices system time
  10. At the calculated time playback starts

If you only need accuracy to within one minute a public api already exists. Otherwise the server will be a private endpoint you create yourself.

Note: As you appear to be aiming for synchronised playback and different devices will have different performance you need to completely prepare (load/buffer) your media player before playback begins. Probably immediately when the song is received, before synchronising time

Upvotes: 1

serv-inc
serv-inc

Reputation: 38307

So how can I sync the clocks of two android phones? How can I solve the problem?

Conceptually, you can skip synchronizing the system clocks, and just determine the relative offset and drift. In the paper Tiny-Sync: Tight Time Synchronization for Wireless Sensor Networks, the authors first describe node i's time as

ti (t) = ai t + bi

in terms of UTC t.

where ai and bi are the drift and the offset of node i’s clock.

From this, you can express node 1's time t1 in terms of node 2's time t2, as

t1 (t) = a12 t2 (t) + b12.

To estimate these coefficients, messages are sent, as depicted here:

message from 1 to 2 and back to 1

Each message sender adds a timestamp. With as few as two data points, the offset, and the drift of the two nodes can be estimated, with a tree-based method to synchronize a network of nodes.

As of the paper,

the algorithms provide very good precision (microsecond if crafted carefully) and bounds on the precision while using very limited resources, [emphasis mine]

Upvotes: 3

Related Questions