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         // Broadcast alarms sent by system are immutable
107         mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent,
108                 PendingIntent.FLAG_IMMUTABLE);
109 
110         mPollingIntervalMs = mContext.getResources().getInteger(
111                 com.android.internal.R.integer.config_ntpPollingInterval);
112         mPollingIntervalShorterMs = mContext.getResources().getInteger(
113                 com.android.internal.R.integer.config_ntpPollingIntervalShorter);
114         mTryAgainTimesMax = mContext.getResources().getInteger(
115                 com.android.internal.R.integer.config_ntpRetry);
116 
117         mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
118                 PowerManager.PARTIAL_WAKE_LOCK, TAG);
119     }
120 
121     /** Initialize the receivers and initiate the first NTP request */
systemRunning()122     public void systemRunning() {
123         registerForAlarms();
124 
125         HandlerThread thread = new HandlerThread(TAG);
126         thread.start();
127         mHandler = new MyHandler(thread.getLooper());
128         mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
129         mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
130 
131         mAutoTimeSettingObserver = new AutoTimeSettingObserver(mContext, mHandler,
132                 EVENT_AUTO_TIME_ENABLED);
133         mAutoTimeSettingObserver.observe();
134     }
135 
registerForAlarms()136     private void registerForAlarms() {
137         mContext.registerReceiver(
138                 new BroadcastReceiver() {
139                     @Override
140                     public void onReceive(Context context, Intent intent) {
141                         mHandler.obtainMessage(EVENT_POLL_NETWORK_TIME).sendToTarget();
142                     }
143                 }, new IntentFilter(ACTION_POLL));
144     }
145 
onPollNetworkTime(int event)146     private void onPollNetworkTime(int event) {
147         // If we don't have any default network, don't bother.
148         if (mDefaultNetwork == null) return;
149         mWakeLock.acquire();
150         try {
151             onPollNetworkTimeUnderWakeLock(event);
152         } finally {
153             mWakeLock.release();
154         }
155     }
156 
onPollNetworkTimeUnderWakeLock(int event)157     private void onPollNetworkTimeUnderWakeLock(int event) {
158         // Force an NTP fix when outdated
159         NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
160         if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {
161             if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
162             mTime.forceRefresh();
163             cachedNtpResult = mTime.getCachedTimeResult();
164         }
165 
166         if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) {
167             // Obtained fresh fix; schedule next normal update
168             resetAlarm(mPollingIntervalMs);
169 
170             // Suggest the time to the time detector. It may choose use it to set the system clock.
171             TimestampedValue<Long> timeSignal = new TimestampedValue<>(
172                     cachedNtpResult.getElapsedRealtimeMillis(), cachedNtpResult.getTimeMillis());
173             NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
174             timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateService. event=" + event);
175             mTimeDetector.suggestNetworkTime(timeSuggestion);
176         } else {
177             // No fresh fix; schedule retry
178             mTryAgainCounter++;
179             if (mTryAgainTimesMax < 0 || mTryAgainCounter <= mTryAgainTimesMax) {
180                 resetAlarm(mPollingIntervalShorterMs);
181             } else {
182                 // Try much later
183                 mTryAgainCounter = 0;
184                 resetAlarm(mPollingIntervalMs);
185             }
186         }
187     }
188 
189     /**
190      * Cancel old alarm and starts a new one for the specified interval.
191      *
192      * @param interval when to trigger the alarm, starting from now.
193      */
resetAlarm(long interval)194     private void resetAlarm(long interval) {
195         mAlarmManager.cancel(mPendingPollIntent);
196         long now = SystemClock.elapsedRealtime();
197         long next = now + interval;
198         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
199     }
200 
201     /** Handler to do the network accesses on */
202     private class MyHandler extends Handler {
203 
MyHandler(Looper l)204         MyHandler(Looper l) {
205             super(l);
206         }
207 
208         @Override
handleMessage(Message msg)209         public void handleMessage(Message msg) {
210             switch (msg.what) {
211                 case EVENT_AUTO_TIME_ENABLED:
212                 case EVENT_POLL_NETWORK_TIME:
213                 case EVENT_NETWORK_CHANGED:
214                     onPollNetworkTime(msg.what);
215                     break;
216             }
217         }
218     }
219 
220     private class NetworkTimeUpdateCallback extends NetworkCallback {
221         @Override
onAvailable(Network network)222         public void onAvailable(Network network) {
223             Log.d(TAG, String.format("New default network %s; checking time.", network));
224             mDefaultNetwork = network;
225             // Running on mHandler so invoke directly.
226             onPollNetworkTime(EVENT_NETWORK_CHANGED);
227         }
228 
229         @Override
onLost(Network network)230         public void onLost(Network network) {
231             if (network.equals(mDefaultNetwork)) mDefaultNetwork = null;
232         }
233     }
234 
235     /**
236      * Observer to watch for changes to the AUTO_TIME setting. It only triggers when the setting
237      * is enabled.
238      */
239     private static class AutoTimeSettingObserver extends ContentObserver {
240 
241         private final Context mContext;
242         private final int mMsg;
243         private final Handler mHandler;
244 
AutoTimeSettingObserver(Context context, Handler handler, int msg)245         AutoTimeSettingObserver(Context context, Handler handler, int msg) {
246             super(handler);
247             mContext = context;
248             mHandler = handler;
249             mMsg = msg;
250         }
251 
observe()252         void observe() {
253             ContentResolver resolver = mContext.getContentResolver();
254             resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
255                     false, this);
256         }
257 
258         @Override
onChange(boolean selfChange)259         public void onChange(boolean selfChange) {
260             if (isAutomaticTimeEnabled()) {
261                 mHandler.obtainMessage(mMsg).sendToTarget();
262             }
263         }
264 
265         /**
266          * Checks if the user prefers to automatically set the time.
267          */
isAutomaticTimeEnabled()268         private boolean isAutomaticTimeEnabled() {
269             ContentResolver resolver = mContext.getContentResolver();
270             return Settings.Global.getInt(resolver, Settings.Global.AUTO_TIME, 0) != 0;
271         }
272     }
273 
274     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)275     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
276         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
277         pw.print("PollingIntervalMs: ");
278         TimeUtils.formatDuration(mPollingIntervalMs, pw);
279         pw.print("\nPollingIntervalShorterMs: ");
280         TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
281         pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
282         pw.println("\nTryAgainCounter: " + mTryAgainCounter);
283         NtpTrustedTime.TimeResult ntpResult = mTime.getCachedTimeResult();
284         pw.println("NTP cache result: " + ntpResult);
285         if (ntpResult != null) {
286             pw.println("NTP result age: " + ntpResult.getAgeMillis());
287         }
288         pw.println();
289     }
290 }
291