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