blob: 296b500eaceea0bf8938ec9cc0b6c475b1173542 [file] [log] [blame]
Yu-Han Yangc87c4c32018-02-20 17:05:59 -08001package com.android.server.location;
2
3import android.content.Context;
4import android.net.ConnectivityManager;
5import android.net.NetworkInfo;
6import android.os.Handler;
7import android.os.Looper;
8import android.os.PowerManager;
9import android.os.PowerManager.WakeLock;
10import android.util.Log;
11import android.util.NtpTrustedTime;
12
13import com.android.internal.annotations.GuardedBy;
14import com.android.internal.annotations.VisibleForTesting;
15
16import java.util.Date;
17
18/**
19 * Handles inject NTP time to GNSS.
20 *
21 * <p>The client is responsible to call {@link #onNetworkAvailable()} when network is available
22 * for retrieving NTP Time.
23 */
24class NtpTimeHelper {
25
26 private static final String TAG = "NtpTimeHelper";
27 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
28
29 // states for injecting ntp
30 private static final int STATE_PENDING_NETWORK = 0;
31 private static final int STATE_RETRIEVING_AND_INJECTING = 1;
32 private static final int STATE_IDLE = 2;
33
34 // how often to request NTP time, in milliseconds
35 // current setting 24 hours
36 @VisibleForTesting
37 static final long NTP_INTERVAL = 24 * 60 * 60 * 1000;
38
39 // how long to wait if we have a network error in NTP
40 // the initial value of the exponential backoff
41 // current setting - 5 minutes
42 @VisibleForTesting
43 static final long RETRY_INTERVAL = 5 * 60 * 1000;
44 // how long to wait if we have a network error in NTP
45 // the max value of the exponential backoff
46 // current setting - 4 hours
47 private static final long MAX_RETRY_INTERVAL = 4 * 60 * 60 * 1000;
48
49 private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
50 private static final String WAKELOCK_KEY = "NtpTimeHelper";
51
52 private final ExponentialBackOff mNtpBackOff = new ExponentialBackOff(RETRY_INTERVAL,
53 MAX_RETRY_INTERVAL);
54
55 private final ConnectivityManager mConnMgr;
56 private final NtpTrustedTime mNtpTime;
57 private final WakeLock mWakeLock;
58 private final Handler mHandler;
59
60 @GuardedBy("this")
61 private final InjectNtpTimeCallback mCallback;
62
63 // flags to trigger NTP when network becomes available
64 // initialized to STATE_PENDING_NETWORK so we do NTP when the network comes up after booting
65 @GuardedBy("this")
66 private int mInjectNtpTimeState = STATE_PENDING_NETWORK;
67
68 // set to true if the GPS engine requested on-demand NTP time requests
69 @GuardedBy("this")
70 private boolean mOnDemandTimeInjection;
71
72 interface InjectNtpTimeCallback {
73 void injectTime(long time, long timeReference, int uncertainty);
74 }
75
76 @VisibleForTesting
77 NtpTimeHelper(Context context, Looper looper, InjectNtpTimeCallback callback,
78 NtpTrustedTime ntpTime) {
79 mConnMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
80 mCallback = callback;
81 mNtpTime = ntpTime;
82 mHandler = new Handler(looper);
83 PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
84 mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
85 }
86
87 NtpTimeHelper(Context context, Looper looper, InjectNtpTimeCallback callback) {
88 this(context, looper, callback, NtpTrustedTime.getInstance(context));
89 }
90
91 synchronized void enablePeriodicTimeInjection() {
92 mOnDemandTimeInjection = true;
93 }
94
95 synchronized void onNetworkAvailable() {
96 if (mInjectNtpTimeState == STATE_PENDING_NETWORK) {
97 retrieveAndInjectNtpTime();
98 }
99 }
100
101 /**
102 * @return {@code true} if there is a network available for outgoing connections,
103 * {@code false} otherwise.
104 */
105 private boolean isNetworkConnected() {
106 NetworkInfo activeNetworkInfo = mConnMgr.getActiveNetworkInfo();
107 return activeNetworkInfo != null && activeNetworkInfo.isConnected();
108 }
109
110 synchronized void retrieveAndInjectNtpTime() {
111 if (mInjectNtpTimeState == STATE_RETRIEVING_AND_INJECTING) {
112 // already downloading data
113 return;
114 }
115 if (!isNetworkConnected()) {
116 // try again when network is up
117 mInjectNtpTimeState = STATE_PENDING_NETWORK;
118 return;
119 }
120 mInjectNtpTimeState = STATE_RETRIEVING_AND_INJECTING;
121
122 // hold wake lock while task runs
123 mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
124 new Thread(this::blockingGetNtpTimeAndInject).start();
125 }
126
127 /** {@link NtpTrustedTime#forceRefresh} is a blocking network operation. */
128 private void blockingGetNtpTimeAndInject() {
129 long delay;
130
131 // force refresh NTP cache when outdated
132 boolean refreshSuccess = true;
133 if (mNtpTime.getCacheAge() >= NTP_INTERVAL) {
134 // Blocking network operation.
135 refreshSuccess = mNtpTime.forceRefresh();
136 }
137
138 synchronized (this) {
139 mInjectNtpTimeState = STATE_IDLE;
140
141 // only update when NTP time is fresh
142 // If refreshSuccess is false, cacheAge does not drop down.
143 if (mNtpTime.getCacheAge() < NTP_INTERVAL) {
144 long time = mNtpTime.getCachedNtpTime();
145 long timeReference = mNtpTime.getCachedNtpTimeReference();
146 long certainty = mNtpTime.getCacheCertainty();
147
148 if (DEBUG) {
149 long now = System.currentTimeMillis();
150 Log.d(TAG, "NTP server returned: "
151 + time + " (" + new Date(time)
152 + ") reference: " + timeReference
153 + " certainty: " + certainty
154 + " system time offset: " + (time - now));
155 }
156
157 // Ok to cast to int, as can't rollover in practice
158 mHandler.post(() -> mCallback.injectTime(time, timeReference, (int) certainty));
159
160 delay = NTP_INTERVAL;
161 mNtpBackOff.reset();
162 } else {
163 Log.e(TAG, "requestTime failed");
164 delay = mNtpBackOff.nextBackoffMillis();
165 }
166
167 if (DEBUG) {
168 Log.d(TAG, String.format(
169 "onDemandTimeInjection=%s, refreshSuccess=%s, delay=%s",
170 mOnDemandTimeInjection,
171 refreshSuccess,
172 delay));
173 }
174 // TODO(b/73893222): reconcile Capabilities bit 'on demand' name vs. de facto periodic
175 // injection.
176 if (mOnDemandTimeInjection || !refreshSuccess) {
177 /* Schedule next NTP injection.
178 * Since this is delayed, the wake lock is released right away, and will be held
179 * again when the delayed task runs.
180 */
181 mHandler.postDelayed(this::retrieveAndInjectNtpTime, delay);
182 }
183 }
184 try {
185 // release wake lock held by task
186 mWakeLock.release();
187 } catch (Exception e) {
188 // This happens when the WakeLock is already released.
189 }
190 }
191}