blob: 7bdc8a32815aab1b1d4be8a60554b4ae86787302 [file] [log] [blame]
Neil Fuller4773b9d2018-06-08 18:44:49 +01001/*
2 * Copyright (C) 2018 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
17package com.android.server.timedetector;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.app.AlarmManager;
22import android.app.timedetector.TimeSignal;
Neil Fuller4980bbc2018-06-12 21:06:20 +010023import android.content.Intent;
Neil Fuller4773b9d2018-06-08 18:44:49 +010024import android.util.Slog;
Neil Fuller4980bbc2018-06-12 21:06:20 +010025import android.util.TimestampedValue;
Neil Fuller4773b9d2018-06-08 18:44:49 +010026
Neil Fuller4980bbc2018-06-12 21:06:20 +010027import com.android.internal.telephony.TelephonyIntents;
28
Neil Fuller4773b9d2018-06-08 18:44:49 +010029import java.io.PrintWriter;
30
31/**
Neil Fuller4980bbc2018-06-12 21:06:20 +010032 * An implementation of TimeDetectorStrategy that passes only NITZ suggestions to
33 * {@link AlarmManager}. The TimeDetectorService handles thread safety: all calls to
34 * this class can be assumed to be single threaded (though the thread used may vary).
Neil Fuller4773b9d2018-06-08 18:44:49 +010035 */
Neil Fuller4980bbc2018-06-12 21:06:20 +010036// @NotThreadSafe
Neil Fuller4773b9d2018-06-08 18:44:49 +010037public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
38
39 private final static String TAG = "timedetector.SimpleTimeDetectorStrategy";
40
Neil Fuller4980bbc2018-06-12 21:06:20 +010041 /**
42 * CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the
43 * actual system clock time before a warning is logged. Used to help identify situations where
44 * there is something other than this class setting the system clock.
45 */
46 private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000;
47
48 // @NonNull after initialize()
49 private Callback mCallback;
50
51 // NITZ state.
52 @Nullable private TimestampedValue<Long> mLastNitzTime;
53
54
55 // Information about the last time signal received: Used when toggling auto-time.
56 @Nullable private TimestampedValue<Long> mLastSystemClockTime;
57 private boolean mLastSystemClockTimeSendNetworkBroadcast;
58
59 // System clock state.
60 @Nullable private TimestampedValue<Long> mLastSystemClockTimeSet;
Neil Fuller4773b9d2018-06-08 18:44:49 +010061
62 @Override
63 public void initialize(@NonNull Callback callback) {
Neil Fuller4980bbc2018-06-12 21:06:20 +010064 mCallback = callback;
Neil Fuller4773b9d2018-06-08 18:44:49 +010065 }
66
67 @Override
68 public void suggestTime(@NonNull TimeSignal timeSignal) {
69 if (!TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId())) {
Neil Fuller4980bbc2018-06-12 21:06:20 +010070 Slog.w(TAG, "Ignoring signal from unsupported source: " + timeSignal);
Neil Fuller4773b9d2018-06-08 18:44:49 +010071 return;
72 }
73
Neil Fuller4980bbc2018-06-12 21:06:20 +010074 // NITZ logic
75
76 TimestampedValue<Long> newNitzUtcTime = timeSignal.getUtcTime();
77 boolean nitzTimeIsValid = validateNewNitzTime(newNitzUtcTime, mLastNitzTime);
78 if (!nitzTimeIsValid) {
79 return;
80 }
81 // Always store the last NITZ value received, regardless of whether we go on to use it to
82 // update the system clock. This is so that we can validate future NITZ signals.
83 mLastNitzTime = newNitzUtcTime;
84
85 // System clock update logic.
86
87 // Historically, Android has sent a telephony broadcast only when setting the time using
88 // NITZ.
89 final boolean sendNetworkBroadcast =
90 TimeSignal.SOURCE_ID_NITZ.equals(timeSignal.getSourceId());
91
92 final TimestampedValue<Long> newUtcTime = newNitzUtcTime;
93 setSystemClockIfRequired(newUtcTime, sendNetworkBroadcast);
94 }
95
96 private static boolean validateNewNitzTime(TimestampedValue<Long> newNitzUtcTime,
97 TimestampedValue<Long> lastNitzTime) {
98
99 if (lastNitzTime != null) {
100 long referenceTimeDifference =
101 TimestampedValue.referenceTimeDifference(newNitzUtcTime, lastNitzTime);
102 if (referenceTimeDifference < 0 || referenceTimeDifference > Integer.MAX_VALUE) {
103 // Out of order or bogus.
104 Slog.w(TAG, "validateNewNitzTime: Bad NITZ signal received."
105 + " referenceTimeDifference=" + referenceTimeDifference
106 + " lastNitzTime=" + lastNitzTime
107 + " newNitzUtcTime=" + newNitzUtcTime);
108 return false;
109 }
110 }
111 return true;
112 }
113
114 private void setSystemClockIfRequired(
115 TimestampedValue<Long> time, boolean sendNetworkBroadcast) {
116
117 // Store the last candidate we've seen in all cases so we can set the system clock
118 // when/if time detection is enabled.
119 mLastSystemClockTime = time;
120 mLastSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast;
121
122 if (!mCallback.isTimeDetectionEnabled()) {
123 Slog.d(TAG, "setSystemClockIfRequired: Time detection is not enabled. time=" + time);
124 return;
125 }
126
127 mCallback.acquireWakeLock();
128 try {
129 long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
130 long actualTimeMillis = mCallback.systemClockMillis();
131
132 // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else
133 // may be setting the clock.
134 if (mLastSystemClockTimeSet != null) {
135 long expectedTimeMillis = TimeDetectorStrategy.getTimeAt(
136 mLastSystemClockTimeSet, elapsedRealtimeMillis);
137 long absSystemClockDifference = Math.abs(expectedTimeMillis - actualTimeMillis);
138 if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) {
139 Slog.w(TAG, "System clock has not tracked elapsed real time clock. A clock may"
140 + " be inaccurate or something unexpectedly set the system clock."
141 + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
142 + " expectedTimeMillis=" + expectedTimeMillis
143 + " actualTimeMillis=" + actualTimeMillis);
144 }
145 }
146
147 final String reason = "New time signal";
148 adjustAndSetDeviceSystemClock(
149 time, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, reason);
150 } finally {
151 mCallback.releaseWakeLock();
152 }
Neil Fuller4773b9d2018-06-08 18:44:49 +0100153 }
154
155 @Override
Neil Fuller4980bbc2018-06-12 21:06:20 +0100156 public void handleAutoTimeDetectionToggle(boolean enabled) {
157 // If automatic time detection is enabled we update the system clock instantly if we can.
158 // Conversely, if automatic time detection is disabled we leave the clock as it is.
159 if (enabled) {
160 if (mLastSystemClockTime != null) {
161 // Only send the network broadcast if the last candidate would have caused one.
162 final boolean sendNetworkBroadcast = mLastSystemClockTimeSendNetworkBroadcast;
163
164 mCallback.acquireWakeLock();
165 try {
166 long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
167 long actualTimeMillis = mCallback.systemClockMillis();
168
169 final String reason = "Automatic time detection enabled.";
170 adjustAndSetDeviceSystemClock(mLastSystemClockTime, sendNetworkBroadcast,
171 elapsedRealtimeMillis, actualTimeMillis, reason);
172 } finally {
173 mCallback.releaseWakeLock();
174 }
175 }
176 } else {
177 // CLOCK_PARANOIA: We are losing "control" of the system clock so we cannot predict what
178 // it should be in future.
179 mLastSystemClockTimeSet = null;
180 }
181 }
182
183 @Override
184 public void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
185 pw.println("mLastNitzTime=" + mLastNitzTime);
186 pw.println("mLastSystemClockTimeSet=" + mLastSystemClockTimeSet);
187 pw.println("mLastSystemClockTime=" + mLastSystemClockTime);
188 pw.println("mLastSystemClockTimeSendNetworkBroadcast="
189 + mLastSystemClockTimeSendNetworkBroadcast);
190 }
191
192 private void adjustAndSetDeviceSystemClock(
193 TimestampedValue<Long> newTime, boolean sendNetworkBroadcast,
194 long elapsedRealtimeMillis, long actualSystemClockMillis, String reason) {
195
196 // Adjust for the time that has elapsed since the signal was received.
197 long newSystemClockMillis = TimeDetectorStrategy.getTimeAt(newTime, elapsedRealtimeMillis);
198
199 // Check if the new signal would make sufficient difference to the system clock. If it's
200 // below the threshold then ignore it.
201 long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis);
202 long systemClockUpdateThreshold = mCallback.systemClockUpdateThresholdMillis();
203 if (absTimeDifference < systemClockUpdateThreshold) {
204 Slog.d(TAG, "adjustAndSetDeviceSystemClock: Not setting system clock. New time and"
205 + " system clock are close enough."
206 + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
207 + " newTime=" + newTime
208 + " reason=" + reason
209 + " systemClockUpdateThreshold=" + systemClockUpdateThreshold
210 + " absTimeDifference=" + absTimeDifference);
211 return;
212 }
213
214 Slog.d(TAG, "Setting system clock using time=" + newTime
215 + " reason=" + reason
216 + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
217 + " newTimeMillis=" + newSystemClockMillis);
218 mCallback.setSystemClock(newSystemClockMillis);
219
220 // CLOCK_PARANOIA : Record the last time this class set the system clock.
221 mLastSystemClockTimeSet = newTime;
222
223 if (sendNetworkBroadcast) {
224 // Send a broadcast that telephony code used to send after setting the clock.
225 // TODO Remove this broadcast as soon as there are no remaining listeners.
226 Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
227 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
228 intent.putExtra("time", newSystemClockMillis);
229 mCallback.sendStickyBroadcast(intent);
230 }
Neil Fuller4773b9d2018-06-08 18:44:49 +0100231 }
232}