1b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey/*
2b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey * Copyright (C) 2011 The Android Open Source Project
3b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey *
4b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
5b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey * you may not use this file except in compliance with the License.
6b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey * You may obtain a copy of the License at
7b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey *
8b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
9b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey *
10b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey * Unless required by applicable law or agreed to in writing, software
11b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
12b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey * See the License for the specific language governing permissions and
14b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey * limitations under the License.
15b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey */
16b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey
17b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkeypackage android.util;
18b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey
19104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkeyimport android.content.ContentResolver;
20104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkeyimport android.content.Context;
21104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkeyimport android.content.res.Resources;
22b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkeyimport android.net.SntpClient;
23b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkeyimport android.os.SystemClock;
24104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkeyimport android.provider.Settings;
25b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey
26b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey/**
27104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey * {@link TrustedTime} that connects with a remote NTP server as its trusted
28104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey * time source.
29b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey *
30b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey * @hide
31b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey */
32b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkeypublic class NtpTrustedTime implements TrustedTime {
33104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey    private static final String TAG = "NtpTrustedTime";
34104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey    private static final boolean LOGD = false;
35104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey
36104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey    private static NtpTrustedTime sSingleton;
37104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey
38104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey    private final String mServer;
39104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey    private final long mTimeout;
40b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey
41b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    private boolean mHasCache;
42b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    private long mCachedNtpTime;
43b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    private long mCachedNtpElapsedRealtime;
44b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    private long mCachedNtpCertainty;
45b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey
46104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey    private NtpTrustedTime(String server, long timeout) {
47104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey        if (LOGD) Log.d(TAG, "creating NtpTrustedTime using " + server);
48104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey        mServer = server;
49104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey        mTimeout = timeout;
50b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    }
51b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey
52104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey    public static synchronized NtpTrustedTime getInstance(Context context) {
53104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey        if (sSingleton == null) {
54104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey            final Resources res = context.getResources();
55104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey            final ContentResolver resolver = context.getContentResolver();
56104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey
57104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey            final String defaultServer = res.getString(
58104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey                    com.android.internal.R.string.config_ntpServer);
59104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey            final long defaultTimeout = res.getInteger(
60104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey                    com.android.internal.R.integer.config_ntpTimeout);
61104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey
62e6e6197d7c2eb5c29224bcddb0131a302267f6deJeff Sharkey            final String secureServer = Settings.Global.getString(
63023c05a341b87d0899c89bf355b6ae27d138bb03Jeff Sharkey                    resolver, Settings.Global.NTP_SERVER);
64e6e6197d7c2eb5c29224bcddb0131a302267f6deJeff Sharkey            final long timeout = Settings.Global.getLong(
65023c05a341b87d0899c89bf355b6ae27d138bb03Jeff Sharkey                    resolver, Settings.Global.NTP_TIMEOUT, defaultTimeout);
66104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey
67104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey            final String server = secureServer != null ? secureServer : defaultServer;
68104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey            sSingleton = new NtpTrustedTime(server, timeout);
69104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey        }
70104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey
71104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey        return sSingleton;
72b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    }
73b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey
74023c05a341b87d0899c89bf355b6ae27d138bb03Jeff Sharkey    @Override
75b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    public boolean forceRefresh() {
76104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey        if (mServer == null) {
77f055829f358f995efe3964706860f101a129f5e3Jeff Sharkey            // missing server, so no trusted time available
78f055829f358f995efe3964706860f101a129f5e3Jeff Sharkey            return false;
79b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        }
80b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey
81104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey        if (LOGD) Log.d(TAG, "forceRefresh() from cache miss");
82b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        final SntpClient client = new SntpClient();
83104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey        if (client.requestTime(mServer, (int) mTimeout)) {
84b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey            mHasCache = true;
85b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey            mCachedNtpTime = client.getNtpTime();
86b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey            mCachedNtpElapsedRealtime = client.getNtpTimeReference();
87b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey            mCachedNtpCertainty = client.getRoundTripTime() / 2;
88b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey            return true;
89b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        } else {
90b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey            return false;
91b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        }
92b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    }
93b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey
94023c05a341b87d0899c89bf355b6ae27d138bb03Jeff Sharkey    @Override
95b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    public boolean hasCache() {
96b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        return mHasCache;
97b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    }
98b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey
99023c05a341b87d0899c89bf355b6ae27d138bb03Jeff Sharkey    @Override
100b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    public long getCacheAge() {
101b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        if (mHasCache) {
102b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey            return SystemClock.elapsedRealtime() - mCachedNtpElapsedRealtime;
103b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        } else {
104b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey            return Long.MAX_VALUE;
105b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        }
106b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    }
107b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey
108023c05a341b87d0899c89bf355b6ae27d138bb03Jeff Sharkey    @Override
109b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    public long getCacheCertainty() {
110b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        if (mHasCache) {
111b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey            return mCachedNtpCertainty;
112b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        } else {
113b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey            return Long.MAX_VALUE;
114b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        }
115b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    }
116b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey
117023c05a341b87d0899c89bf355b6ae27d138bb03Jeff Sharkey    @Override
118b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    public long currentTimeMillis() {
119b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        if (!mHasCache) {
120b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey            throw new IllegalStateException("Missing authoritative time source");
121b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        }
122104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey        if (LOGD) Log.d(TAG, "currentTimeMillis() cache hit");
123b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey
124b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        // current time is age after the last ntp cache; callers who
125b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        // want fresh values will hit makeAuthoritative() first.
126b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey        return mCachedNtpTime + getCacheAge();
127b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey    }
128104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey
129104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey    public long getCachedNtpTime() {
130104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey        if (LOGD) Log.d(TAG, "getCachedNtpTime() cache hit");
131104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey        return mCachedNtpTime;
132104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey    }
133104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey
134104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey    public long getCachedNtpTimeReference() {
135104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey        return mCachedNtpElapsedRealtime;
136104344e507610be42fb70c7deda3c422c543bfcbJeff Sharkey    }
137b7342acebcb7e5dc7da0cda77fbddf50e7dfdd7cJeff Sharkey}
138