1 /*
2  * Copyright (C) 2011 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 
17 package android.util;
18 
19 import android.content.ContentResolver;
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.net.ConnectivityManager;
23 import android.net.Network;
24 import android.net.NetworkInfo;
25 import android.net.SntpClient;
26 import android.os.SystemClock;
27 import android.provider.Settings;
28 import android.text.TextUtils;
29 
30 /**
31  * {@link TrustedTime} that connects with a remote NTP server as its trusted
32  * time source.
33  *
34  * @hide
35  */
36 public class NtpTrustedTime implements TrustedTime {
37     private static final String TAG = "NtpTrustedTime";
38     private static final boolean LOGD = false;
39 
40     private static NtpTrustedTime sSingleton;
41     private static Context sContext;
42 
43     private final String mServer;
44     private final long mTimeout;
45 
46     private ConnectivityManager mCM;
47 
48     private boolean mHasCache;
49     private long mCachedNtpTime;
50     private long mCachedNtpElapsedRealtime;
51     private long mCachedNtpCertainty;
52 
NtpTrustedTime(String server, long timeout)53     private NtpTrustedTime(String server, long timeout) {
54         if (LOGD) Log.d(TAG, "creating NtpTrustedTime using " + server);
55         mServer = server;
56         mTimeout = timeout;
57     }
58 
getInstance(Context context)59     public static synchronized NtpTrustedTime getInstance(Context context) {
60         if (sSingleton == null) {
61             final Resources res = context.getResources();
62             final ContentResolver resolver = context.getContentResolver();
63 
64             final String defaultServer = res.getString(
65                     com.android.internal.R.string.config_ntpServer);
66             final long defaultTimeout = res.getInteger(
67                     com.android.internal.R.integer.config_ntpTimeout);
68 
69             final String secureServer = Settings.Global.getString(
70                     resolver, Settings.Global.NTP_SERVER);
71             final long timeout = Settings.Global.getLong(
72                     resolver, Settings.Global.NTP_TIMEOUT, defaultTimeout);
73 
74             final String server = secureServer != null ? secureServer : defaultServer;
75             sSingleton = new NtpTrustedTime(server, timeout);
76             sContext = context;
77         }
78 
79         return sSingleton;
80     }
81 
82     @Override
forceRefresh()83     public boolean forceRefresh() {
84         // We can't do this at initialization time: ConnectivityService might not be running yet.
85         synchronized (this) {
86             if (mCM == null) {
87                 mCM = sContext.getSystemService(ConnectivityManager.class);
88             }
89         }
90 
91         final Network network = mCM == null ? null : mCM.getActiveNetwork();
92         return forceRefresh(network);
93     }
94 
forceRefresh(Network network)95     public boolean forceRefresh(Network network) {
96         if (TextUtils.isEmpty(mServer)) {
97             // missing server, so no trusted time available
98             return false;
99         }
100 
101         // We can't do this at initialization time: ConnectivityService might not be running yet.
102         synchronized (this) {
103             if (mCM == null) {
104                 mCM = sContext.getSystemService(ConnectivityManager.class);
105             }
106         }
107 
108         final NetworkInfo ni = mCM == null ? null : mCM.getNetworkInfo(network);
109         if (ni == null || !ni.isConnected()) {
110             if (LOGD) Log.d(TAG, "forceRefresh: no connectivity");
111             return false;
112         }
113 
114 
115         if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
116         final SntpClient client = new SntpClient();
117         if (client.requestTime(mServer, (int) mTimeout, network)) {
118             mHasCache = true;
119             mCachedNtpTime = client.getNtpTime();
120             mCachedNtpElapsedRealtime = client.getNtpTimeReference();
121             mCachedNtpCertainty = client.getRoundTripTime() / 2;
122             return true;
123         } else {
124             return false;
125         }
126     }
127 
128     @Override
hasCache()129     public boolean hasCache() {
130         return mHasCache;
131     }
132 
133     @Override
getCacheAge()134     public long getCacheAge() {
135         if (mHasCache) {
136             return SystemClock.elapsedRealtime() - mCachedNtpElapsedRealtime;
137         } else {
138             return Long.MAX_VALUE;
139         }
140     }
141 
142     @Override
getCacheCertainty()143     public long getCacheCertainty() {
144         if (mHasCache) {
145             return mCachedNtpCertainty;
146         } else {
147             return Long.MAX_VALUE;
148         }
149     }
150 
151     @Override
currentTimeMillis()152     public long currentTimeMillis() {
153         if (!mHasCache) {
154             throw new IllegalStateException("Missing authoritative time source");
155         }
156         if (LOGD) Log.d(TAG, "currentTimeMillis() cache hit");
157 
158         // current time is age after the last ntp cache; callers who
159         // want fresh values will hit makeAuthoritative() first.
160         return mCachedNtpTime + getCacheAge();
161     }
162 
getCachedNtpTime()163     public long getCachedNtpTime() {
164         if (LOGD) Log.d(TAG, "getCachedNtpTime() cache hit");
165         return mCachedNtpTime;
166     }
167 
getCachedNtpTimeReference()168     public long getCachedNtpTimeReference() {
169         return mCachedNtpElapsedRealtime;
170     }
171 }
172