1 /*
2  * Copyright (C) 2010 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 com.android.server;
18 
19 import android.app.AlarmManager;
20 import android.app.PendingIntent;
21 import android.app.timedetector.NetworkTimeSuggestion;
22 import android.app.timedetector.TimeDetector;
23 import android.content.BroadcastReceiver;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.database.ContentObserver;
29 import android.net.ConnectivityManager;
30 import android.net.ConnectivityManager.NetworkCallback;
31 import android.net.Network;
32 import android.os.Binder;
33 import android.os.Handler;
34 import android.os.HandlerThread;
35 import android.os.Looper;
36 import android.os.Message;
37 import android.os.PowerManager;
38 import android.os.SystemClock;
39 import android.os.TimestampedValue;
40 import android.provider.Settings;
41 import android.util.Log;
42 import android.util.NtpTrustedTime;
43 import android.util.TimeUtils;
44 
45 import com.android.internal.util.DumpUtils;
46 
47 import java.io.FileDescriptor;
48 import java.io.PrintWriter;
49 
50 /**
51  * Monitors the network time. If looking up the network time fails for some reason, it tries a few
52  * times with a short interval and then resets to checking on longer intervals.
53  *
54  * <p>When available, the time is always suggested to the {@link
55  * com.android.server.timedetector.TimeDetectorService} where it may be used to set the device
56  * system clock, depending on user settings and what other signals are available.
57  */
58 public class NetworkTimeUpdateService extends Binder {
59 
60     private static final String TAG = "NetworkTimeUpdateService";
61     private static final boolean DBG = false;
62 
63     private static final int EVENT_AUTO_TIME_ENABLED = 1;
64     private static final int EVENT_POLL_NETWORK_TIME = 2;
65     private static final int EVENT_NETWORK_CHANGED = 3;
66 
67     private static final String ACTION_POLL =
68             "com.android.server.NetworkTimeUpdateService.action.POLL";
69 
70     private static final int POLL_REQUEST = 0;
71 
72     private Network mDefaultNetwork = null;
73 
74     private final Context mContext;
75     private final NtpTrustedTime mTime;
76     private final AlarmManager mAlarmManager;
77     private final TimeDetector mTimeDetector;
78     private final ConnectivityManager mCM;
79     private final PendingIntent mPendingPollIntent;
80     private final PowerManager.WakeLock mWakeLock;
81 
82     // NTP lookup is done on this thread and handler
83     private Handler mHandler;
84     private AutoTimeSettingObserver mAutoTimeSettingObserver;
85     private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
86 
87     // Normal polling frequency
88     private final long mPollingIntervalMs;
89     // Try-again polling interval, in case the network request failed
90     private final long mPollingIntervalShorterMs;
91     // Number of times to try again
92     private final int mTryAgainTimesMax;
93     // Keeps track of how many quick attempts were made to fetch NTP time.
94     // During bootup, the network may not have been up yet, or it's taking time for the
95     // connection to happen.
96     private int mTryAgainCounter;
97 
NetworkTimeUpdateService(Context context)98     public NetworkTimeUpdateService(Context context) {
99         mContext = context;
100         mTime = NtpTrustedTime.getInstance(context);
101         mAlarmManager = mContext.getSystemService(AlarmManager.class);
102         mTimeDetector = mContext.getSystemService(TimeDetector.class);
103         mCM = mContext.getSystemService(ConnectivityManager.class);
104 
105         Intent pollIntent = new Intent(ACTION_POLL, null);
106         mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0);
107 
108         mPollingIntervalMs = mContext.getResources().getInteger(
109                 com.android.internal.R.integer.config_ntpPollingInterval);
110         mPollingIntervalShorterMs = mContext.getResources().getInteger(
111                 com.android.internal.R.integer.config_ntpPollingIntervalShorter);
112         mTryAgainTimesMax = mContext.getResources().getInteger(
113                 com.android.internal.R.integer.config_ntpRetry);
114 
115         mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
116                 PowerManager.PARTIAL_WAKE_LOCK, TAG);
117     }
118 
119     /** Initialize the receivers and initiate the first NTP request */
systemRunning()120     public void systemRunning() {
121         registerForAlarms();
122 
123         HandlerThread thread = new HandlerThread(TAG);
124         thread.start();
125         mHandler = new MyHandler(thread.getLooper());
126         mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
127         mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
128 
129         mAutoTimeSettingObserver = new AutoTimeSettingObserver(mContext, mHandler,
130                 EVENT_AUTO_TIME_ENABLED);
131         mAutoTimeSettingObserver.observe();
132     }
133 
registerForAlarms()134     private void registerForAlarms() {
135         mContext.registerReceiver(
136                 new BroadcastReceiver() {
137                     @Override
138                     public void onReceive(Context context, Intent intent) {
139                         mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
140                     }
141                 }, new IntentFilter(ACTION_POLL));
142     }
143 
onPollNetworkTime(int event)144     private void onPollNetworkTime(int event) {
145         // If we don't have any default network, don't bother.
146         if (mDefaultNetwork == null) return;
147         mWakeLock.acquire();
148         try {
149             onPollNetworkTimeUnderWakeLock(event);
150         } finally {
151             mWakeLock.release();
152         }
153     }
154 
onPollNetworkTimeUnderWakeLock(int event)155     private void onPollNetworkTimeUnderWakeLock(int event) {
156         // Force an NTP fix when outdated
157         NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
158         if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {
159             if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
160             mTime.forceRefresh();
161             cachedNtpResult = mTime.getCachedTimeResult();
162         }
163 
164         if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) {
165             // Obtained fresh fix; schedule next normal update
166             resetAlarm(mPollingIntervalMs);
167 
168             // Suggest the time to the time detector. It may choose use it to set the system clock.
169             TimestampedValue<Long> timeSignal = new TimestampedValue<>(
170                     cachedNtpResult.getElapsedRealtimeMillis(), cachedNtpResult.getTimeMillis());
171             NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
172             timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateService. event=" + event);
173             mTimeDetector.suggestNetworkTime(timeSuggestion);
174         } else {
175             // No fresh fix; schedule retry
176             mTryAgainCounter++;
177             if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
178                 resetAlarm(mPollingIntervalShorterMs);
179             } else {
180                 // Try much later
181                 mTryAgainCounter = 0;
182                 resetAlarm(mPollingIntervalMs);
183             }
184         }
185     }
186 
187     /**
188      * Cancel old alarm and starts a new one for the specified interval.
189      *
190      * @param interval when to trigger the alarm, starting from now.
191      */
resetAlarm(long interval)192     private void resetAlarm(long interval) {
193         mAlarmManager.cancel(mPendingPollIntent);
194         long now = SystemClock.elapsedRealtime();
195         long next = now + interval;
196         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
197     }
198 
199     /** Handler to do the network accesses on */
200     private class MyHandler extends Handler {
201 
MyHandler(Looper l)202         MyHandler(Looper l) {
203             super(l);
204         }
205 
206         @Override
handleMessage(Message msg)207         public void handleMessage(Message msg) {
208             switch (msg.what) {
209                 case EVENT_AUTO_TIME_ENABLED:
210                 case EVENT_POLL_NETWORK_TIME:
211                 case EVENT_NETWORK_CHANGED:
212                     onPollNetworkTime(msg.what);
213                     break;
214             }
215         }
216     }
217 
218     private class NetworkTimeUpdateCallback extends NetworkCallback {
219         @Override
onAvailable(Network network)220         public void onAvailable(Network network) {
221             Log.d(TAG, String.format("New default network %s; checking time.", network));
222             mDefaultNetwork = network;
223             // Running on mHandler so invoke directly.
224             onPollNetworkTime(EVENT_NETWORK_CHANGED);
225         }
226 
227         @Override
onLost(Network network)228         public void onLost(Network network) {
229             if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
230         }
231     }
232 
233     /**
234      * Observer to watch for changes to the AUTO_TIME setting. It only triggers when the setting
235      * is enabled.
236      */
237     private static class AutoTimeSettingObserver extends ContentObserver {
238 
239         private final Context mContext;
240         private final int mMsg;
241         private final Handler mHandler;
242 
AutoTimeSettingObserver(Context context, Handler handler, int msg)243         AutoTimeSettingObserver(Context context, Handler handler, int msg) {
244             super(handler);
245             mContext = context;
246             mHandler = handler;
247             mMsg = msg;
248         }
249 
observe()250         void observe() {
251             ContentResolver resolver = mContext.getContentResolver();
252             resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
253                     false, this);
254         }
255 
256         @Override
onChange(boolean selfChange)257         public void onChange(boolean selfChange) {
258             if (isAutomaticTimeEnabled()) {
259                 mHandler.obtainMessage(mMsg).sendToTarget();
260             }
261         }
262 
263         /**
264          * Checks if the user prefers to automatically set the time.
265          */
isAutomaticTimeEnabled()266         private boolean isAutomaticTimeEnabled() {
267             ContentResolver resolver = mContext.getContentResolver();
268             return Settings.Global.getInt(resolver, Settings.Global.AUTO_TIME, 0) != 0;
269         }
270     }
271 
272     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)273     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
274         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
275         pw.print("PollingIntervalMs: ");
276         TimeUtils.formatDuration(mPollingIntervalMs, pw);
277         pw.print("\nPollingIntervalShorterMs: ");
278         TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
279         pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
280         pw.println("\nTryAgainCounter: " + mTryAgainCounter);
281         NtpTrustedTime.TimeResult ntpResult = mTime.getCachedTimeResult();
282         pw.println("NTP cache result: " + ntpResult);
283         if (ntpResult != null) {
284             pw.println("NTP result age: " + ntpResult.getAgeMillis());
285         }
286         pw.println();
287     }
288 }
289