前言

由于NTP服务在Android系统中已经存在,但是隐藏的系统API普通的开发手段无法调用。因此通过阅读相关源码就可以整理出我们自己可以使用的逻辑实现。

源码相关文件

  1. frameworks/base/services/java/com/android/server/SystemServer.java
    系统服务类,用于启动管理后续的系统服务,如NetworkTimeUpdateService
  2. frameworks/base/services/java/com/android/server/NetworkTimeUpdateService.java
    通过onPollNetworkTime调用NtpTrustedTime.forceRefresh()获取NTP服务器时间
  3. frameworks/base/core/java/android/util/NtpTrustedTime.java
    创建SntpClient实例,调用SntpClient.requestTime()使用UDP协议请求网络时间
  4. frameworks/base/core/java/android/net/SntpClient.java
    发起服务器请求,并处理返回结果
  5. frameworks/base/core/res/res/values/config.xml
    Android系统配置ntpServer的文件路径

常用的可用的NTP服务器地址

基本官方通过这里查看https://www.pool.ntp.org/zh/zone/asia,但由于一些不可抗拒的原因有可能无法使用,目前整理出的可用的NTP服务器地址列表如下:

"ntp1.aliyun.com", "ntp2.aliyun.com", "ntp3.aliyun.com", "ntp4.aliyun.com", "ntp5.aliyun.com", "ntp6.aliyun.com", "ntp7.aliyun.com", "cn.pool.ntp.org", "cn.ntp.org.cn", "sg.pool.ntp.org", "tw.pool.ntp.org", "jp.pool.ntp.org", "hk.pool.ntp.org", "th.pool.ntp.org", "time.windows.com", "time.nist.gov", "time.apple.com", "time.asia.apple.com", "dns1.synet.edu.cn", "news.neu.edu.cn", "dns.sjtu.edu.cn", "dns2.synet.edu.cn", "ntp.glnet.edu.cn", "s2g.time.edu.cn", "ntp-sz.chl.la", "ntp.gwadar.cn", "3.asia.pool.ntp.org"

复制整理SntpClient.java文件

拿出系统的源码是不能直接使用的,有一些内部日志输出的方法我们也不需要使用,因此替换成android.util.Log的输出结果如下,可直接使用

/* * Copyright (C) 2008 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 com.ziv.demo.ntptimeservice.services;import android.net.Network;import android.os.SystemClock;import android.util.Log;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;import java.net.UnknownHostException;import java.util.Arrays;/** * Simple SNTP client class for retrieving network time. * * Sample usage: * 
SntpClient client = new SntpClient(); * if (client.requestTime("time.foo.com")) { *     long now = client.getNtpTime() + SystemClock.elapsedRealtime() - client.getNtpTimeReference(); * } * 
*/public class SntpClient { private static final String TAG = "SntpClient"; private static final boolean DBG = true; private static final int REFERENCE_TIME_OFFSET = 16; private static final int ORIGINATE_TIME_OFFSET = 24; private static final int RECEIVE_TIME_OFFSET = 32; private static final int TRANSMIT_TIME_OFFSET = 40; private static final int NTP_PACKET_SIZE = 48; private static final int NTP_PORT = 123; private static final int NTP_MODE_CLIENT = 3; private static final int NTP_MODE_SERVER = 4; private static final int NTP_MODE_BROADCAST = 5; private static final int NTP_VERSION = 3; private static final int NTP_LEAP_NOSYNC = 3; private static final int NTP_STRATUM_DEATH = 0; private static final int NTP_STRATUM_MAX = 15; // Number of seconds between Jan 1, 1900 and Jan 1, 1970 // 70 years plus 17 leap days private static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L; // system time computed from NTP server response private long mNtpTime; // value of SystemClock.elapsedRealtime() corresponding to mNtpTime private long mNtpTimeReference; // round trip time in milliseconds private long mRoundTripTime; private static class InvalidServerReplyException extends Exception { public InvalidServerReplyException(String message) { super(message); } } /** * Sends an SNTP request to the given host and processes the response. * * @param host host name of the server. * @param timeout network timeout in milliseconds. * @return true if the transaction was successful. */ public boolean requestTime(String host, int timeout) { DatagramSocket socket = null; InetAddress address = null; try { address = InetAddress.getByName(host); socket = new DatagramSocket(); socket.setSoTimeout(timeout); byte[] buffer = new byte[NTP_PACKET_SIZE]; DatagramPacket request = new DatagramPacket(buffer, buffer.length, address, NTP_PORT); // set mode = 3 (client) and version = 3 // mode is in low 3 bits of first byte // version is in bits 3-5 of first byte buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3); // get current time and write it to the request packet final long requestTime = System.currentTimeMillis(); final long requestTicks = SystemClock.elapsedRealtime(); writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime); socket.send(request); // read the response DatagramPacket response = new DatagramPacket(buffer, buffer.length); socket.receive(response); final long responseTicks = SystemClock.elapsedRealtime(); final long responseTime = requestTime + (responseTicks - requestTicks); // extract the results final byte leap = (byte) ((buffer[0] >> 6) & 0x3); final byte mode = (byte) (buffer[0] & 0x7); final int stratum = (int) (buffer[1] & 0xff); final long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET); final long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET); final long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET); /* do sanity check according to RFC */ // TODO: validate originateTime == requestTime. checkValidServerReply(leap, mode, stratum, transmitTime); long roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime); // receiveTime = originateTime + transit + skew // responseTime = transmitTime + transit - skew long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2; // = ((originateTime + transit + skew - originateTime) + // (transmitTime - (transmitTime + transit - skew)))/2 // = ((transit + skew) + (transmitTime - transmitTime - transit + skew))/2 // = (transit + skew - transit + skew)/2 // = (2 * skew)/2 = skew Log.d(TAG, "Request time form ntp server success, " + address.toString() + " ,roundTripTime: " + roundTripTime); if (DBG) { Log.d(TAG, "round trip: " + roundTripTime + "ms, " + "clock offset: " + clockOffset + "ms"); } // save our results - use the times on this side of the network latency // (response rather than request time) mNtpTime = responseTime + clockOffset; mNtpTimeReference = responseTicks; mRoundTripTime = roundTripTime; } catch (Exception e) {// if (DBG) {// Log.e(TAG, "Error address: " + address.toString());// } Log.e(TAG, "Request time from ntp server failed ,msg: " + e.getMessage()); return false; } finally { if (socket != null) { socket.close(); } } return true; } /** * Returns the time computed from the NTP transaction. * * @return time value computed from NTP server response. */ public long getNtpTime() { return mNtpTime; } /** * Returns the reference clock value (value of SystemClock.elapsedRealtime()) * corresponding to the NTP time. * * @return reference clock corresponding to the NTP time. */ public long getNtpTimeReference() { return mNtpTimeReference; } /** * Returns the round trip time of the NTP transaction * * @return round trip time in milliseconds. */ public long getRoundTripTime() { return mRoundTripTime; } private static void checkValidServerReply( byte leap, byte mode, int stratum, long transmitTime) throws InvalidServerReplyException { if (leap == NTP_LEAP_NOSYNC) { throw new InvalidServerReplyException("unsynchronized server"); } if ((mode != NTP_MODE_SERVER) && (mode != NTP_MODE_BROADCAST)) { throw new InvalidServerReplyException("untrusted mode: " + mode); } if ((stratum == NTP_STRATUM_DEATH) || (stratum > NTP_STRATUM_MAX)) { throw new InvalidServerReplyException("untrusted stratum: " + stratum); } if (transmitTime == 0) { throw new InvalidServerReplyException("zero transmitTime"); } } /** * Reads an unsigned 32 bit big endian number from the given offset in the buffer. */ private long read32(byte[] buffer, int offset) { byte b0 = buffer[offset]; byte b1 = buffer[offset + 1]; byte b2 = buffer[offset + 2]; byte b3 = buffer[offset + 3]; // convert signed bytes to unsigned values int i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0); int i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1); int i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2); int i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3); return ((long) i0 << 24) + ((long) i1 << 16) + ((long) i2 << 8) + (long) i3; } /** * Reads the NTP time stamp at the given offset in the buffer and returns * it as a system time (milliseconds since January 1, 1970). */ private long readTimeStamp(byte[] buffer, int offset) { long seconds = read32(buffer, offset); long fraction = read32(buffer, offset + 4); // Special case: zero means zero. if (seconds == 0 && fraction == 0) { return 0; } return ((seconds - OFFSET_1900_TO_1970) * 1000) + ((fraction * 1000L) / 0x100000000L); } /** * Writes system time (milliseconds since January 1, 1970) as an NTP time stamp * at the given offset in the buffer. */ private void writeTimeStamp(byte[] buffer, int offset, long time) { // Special case: zero means zero. if (time == 0) { Arrays.fill(buffer, offset, offset + 8, (byte) 0x00); return; } long seconds = time / 1000L; long milliseconds = time - seconds * 1000L; seconds += OFFSET_1900_TO_1970; // write seconds in big endian format buffer[offset++] = (byte) (seconds >> 24); buffer[offset++] = (byte) (seconds >> 16); buffer[offset++] = (byte) (seconds >> 8); buffer[offset++] = (byte) (seconds >> 0); long fraction = milliseconds * 0x100000000L / 1000L; // write fraction in big endian format buffer[offset++] = (byte) (fraction >> 24); buffer[offset++] = (byte) (fraction >> 16); buffer[offset++] = (byte) (fraction >> 8); // low order bits should be random data buffer[offset++] = (byte) (Math.random() * 255.0); }}

使用示例

注意,一定要在非主线程调用,并添加网络访问权限

new Thread(){    @Override    public void run() {        SntpClient sntpClient = new SntpClient();        if (sntpClient.requestTime("cn.pool.ntp.org", 30000)) {            long now = sntpClient.getNtpTime() + SystemClock.elapsedRealtime() - sntpClient.getNtpTimeReference();            Date current = new Date(now);            Log.d(TAG, current.toString());        }    }}.start();

结束语

Github地址:https://github.com/Ziv-Android/NTPTimeServiceDemo

RTFSC(Read the Fucking Source Code)

更多相关文章

  1. tcping测试服务器TCP端口
  2. Android通过PHP连接mysql(带源码)
  3. Scroller的使用
  4. Android(安卓)TV native层中Canvas库的实现思路
  5. 探究Android(安卓)View 绘制流程,Canvas 的由来。
  6. android 中的adb命令安装和卸载app、导出手机中的log文件
  7. Android(安卓)ServiceConnection
  8. 64位系统中安装Android(安卓)SDK“系统找不到指定的文件Java.exe
  9. android Configuration系统设置

随机推荐

  1. (trunk的两种方法)(单臂路由)
  2. 马军锋:数据中心SDN解决方案测评报告
  3. 「网络架构」网络代理第一部分: 代理概述
  4. AS:一种低成本高能量密度水溶液锌-硫电池
  5. 传统通信测试厂商借助云计算发力
  6. SDN实战团分享(十九):OpenDaylight在电信网
  7. 「网络架构」网络代理第二部分:Nginx作为
  8. ODL源码分析之flowmod下发流程
  9. SDN实战团分享(二十一):园区网SDN应用分享
  10. AOM:响应可调的动态力致变色器及其在交互