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