|  | /* | 
|  | * Copyright (C) 2011 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 android.util; | 
|  |  | 
|  | import android.content.ContentResolver; | 
|  | import android.content.Context; | 
|  | import android.content.res.Resources; | 
|  | import android.net.ConnectivityManager; | 
|  | import android.net.Network; | 
|  | import android.net.NetworkInfo; | 
|  | import android.net.SntpClient; | 
|  | import android.os.SystemClock; | 
|  | import android.provider.Settings; | 
|  | import android.text.TextUtils; | 
|  |  | 
|  | /** | 
|  | * {@link TrustedTime} that connects with a remote NTP server as its trusted | 
|  | * time source. | 
|  | * | 
|  | * @hide | 
|  | */ | 
|  | public class NtpTrustedTime implements TrustedTime { | 
|  | private static final String TAG = "NtpTrustedTime"; | 
|  | private static final boolean LOGD = false; | 
|  |  | 
|  | private static NtpTrustedTime sSingleton; | 
|  | private static Context sContext; | 
|  |  | 
|  | private final String mServer; | 
|  | private final long mTimeout; | 
|  |  | 
|  | private ConnectivityManager mCM; | 
|  |  | 
|  | private boolean mHasCache; | 
|  | private long mCachedNtpTime; | 
|  | private long mCachedNtpElapsedRealtime; | 
|  | private long mCachedNtpCertainty; | 
|  |  | 
|  | private NtpTrustedTime(String server, long timeout) { | 
|  | if (LOGD) Log.d(TAG, "creating NtpTrustedTime using " + server); | 
|  | mServer = server; | 
|  | mTimeout = timeout; | 
|  | } | 
|  |  | 
|  | public static synchronized NtpTrustedTime getInstance(Context context) { | 
|  | if (sSingleton == null) { | 
|  | final Resources res = context.getResources(); | 
|  | final ContentResolver resolver = context.getContentResolver(); | 
|  |  | 
|  | final String defaultServer = res.getString( | 
|  | com.android.internal.R.string.config_ntpServer); | 
|  | final long defaultTimeout = res.getInteger( | 
|  | com.android.internal.R.integer.config_ntpTimeout); | 
|  |  | 
|  | final String secureServer = Settings.Global.getString( | 
|  | resolver, Settings.Global.NTP_SERVER); | 
|  | final long timeout = Settings.Global.getLong( | 
|  | resolver, Settings.Global.NTP_TIMEOUT, defaultTimeout); | 
|  |  | 
|  | final String server = secureServer != null ? secureServer : defaultServer; | 
|  | sSingleton = new NtpTrustedTime(server, timeout); | 
|  | sContext = context; | 
|  | } | 
|  |  | 
|  | return sSingleton; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean forceRefresh() { | 
|  | // We can't do this at initialization time: ConnectivityService might not be running yet. | 
|  | synchronized (this) { | 
|  | if (mCM == null) { | 
|  | mCM = sContext.getSystemService(ConnectivityManager.class); | 
|  | } | 
|  | } | 
|  |  | 
|  | final Network network = mCM == null ? null : mCM.getActiveNetwork(); | 
|  | return forceRefresh(network); | 
|  | } | 
|  |  | 
|  | public boolean forceRefresh(Network network) { | 
|  | if (TextUtils.isEmpty(mServer)) { | 
|  | // missing server, so no trusted time available | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // We can't do this at initialization time: ConnectivityService might not be running yet. | 
|  | synchronized (this) { | 
|  | if (mCM == null) { | 
|  | mCM = sContext.getSystemService(ConnectivityManager.class); | 
|  | } | 
|  | } | 
|  |  | 
|  | final NetworkInfo ni = mCM == null ? null : mCM.getNetworkInfo(network); | 
|  | if (ni == null || !ni.isConnected()) { | 
|  | if (LOGD) Log.d(TAG, "forceRefresh: no connectivity"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  |  | 
|  | if (LOGD) Log.d(TAG, "forceRefresh() from cache miss"); | 
|  | final SntpClient client = new SntpClient(); | 
|  | if (client.requestTime(mServer, (int) mTimeout, network)) { | 
|  | mHasCache = true; | 
|  | mCachedNtpTime = client.getNtpTime(); | 
|  | mCachedNtpElapsedRealtime = client.getNtpTimeReference(); | 
|  | mCachedNtpCertainty = client.getRoundTripTime() / 2; | 
|  | return true; | 
|  | } else { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean hasCache() { | 
|  | return mHasCache; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public long getCacheAge() { | 
|  | if (mHasCache) { | 
|  | return SystemClock.elapsedRealtime() - mCachedNtpElapsedRealtime; | 
|  | } else { | 
|  | return Long.MAX_VALUE; | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public long getCacheCertainty() { | 
|  | if (mHasCache) { | 
|  | return mCachedNtpCertainty; | 
|  | } else { | 
|  | return Long.MAX_VALUE; | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public long currentTimeMillis() { | 
|  | if (!mHasCache) { | 
|  | throw new IllegalStateException("Missing authoritative time source"); | 
|  | } | 
|  | if (LOGD) Log.d(TAG, "currentTimeMillis() cache hit"); | 
|  |  | 
|  | // current time is age after the last ntp cache; callers who | 
|  | // want fresh values will hit makeAuthoritative() first. | 
|  | return mCachedNtpTime + getCacheAge(); | 
|  | } | 
|  |  | 
|  | public long getCachedNtpTime() { | 
|  | if (LOGD) Log.d(TAG, "getCachedNtpTime() cache hit"); | 
|  | return mCachedNtpTime; | 
|  | } | 
|  |  | 
|  | public long getCachedNtpTimeReference() { | 
|  | return mCachedNtpElapsedRealtime; | 
|  | } | 
|  | } |