Loading core/java/android/app/timedetector/ITimeDetectorService.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app.timedetector; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; /** Loading @@ -33,4 +34,5 @@ import android.app.timedetector.PhoneTimeSuggestion; */ interface ITimeDetectorService { void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion); void suggestManualTime(in ManualTimeSuggestion timeSuggestion); } core/java/android/app/timedetector/ManualTimeSuggestion.aidl 0 → 100644 +19 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app.timedetector; parcelable ManualTimeSuggestion; core/java/android/app/timedetector/ManualTimeSuggestion.java 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app.timedetector; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.util.TimestampedValue; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; /** * A time signal from a manual (user provided) source. The value consists of the number of * milliseconds elapsed since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime * clock when that number was established. The elapsed realtime clock is considered accurate but * volatile, so time signals must not be persisted across device resets. * * @hide */ public final class ManualTimeSuggestion implements Parcelable { public static final @NonNull Creator<ManualTimeSuggestion> CREATOR = new Creator<ManualTimeSuggestion>() { public ManualTimeSuggestion createFromParcel(Parcel in) { return ManualTimeSuggestion.createFromParcel(in); } public ManualTimeSuggestion[] newArray(int size) { return new ManualTimeSuggestion[size]; } }; @NonNull private final TimestampedValue<Long> mUtcTime; @Nullable private ArrayList<String> mDebugInfo; public ManualTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) { mUtcTime = Objects.requireNonNull(utcTime); } private static ManualTimeSuggestion createFromParcel(Parcel in) { TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); ManualTimeSuggestion suggestion = new ManualTimeSuggestion(utcTime); @SuppressWarnings("unchecked") ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); suggestion.mDebugInfo = debugInfo; return suggestion; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeParcelable(mUtcTime, 0); dest.writeList(mDebugInfo); } @NonNull public TimestampedValue<Long> getUtcTime() { return mUtcTime; } @NonNull public List<String> getDebugInfo() { return Collections.unmodifiableList(mDebugInfo); } /** * Associates information with the instance that can be useful for debugging / logging. The * information is present in {@link #toString()} but is not considered for * {@link #equals(Object)} and {@link #hashCode()}. */ public void addDebugInfo(String... debugInfos) { if (mDebugInfo == null) { mDebugInfo = new ArrayList<>(); } mDebugInfo.addAll(Arrays.asList(debugInfos)); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ManualTimeSuggestion that = (ManualTimeSuggestion) o; return Objects.equals(mUtcTime, that.mUtcTime); } @Override public int hashCode() { return Objects.hash(mUtcTime); } @Override public String toString() { return "ManualTimeSuggestion{" + "mUtcTime=" + mUtcTime + ", mDebugInfo=" + mDebugInfo + '}'; } } core/java/android/app/timedetector/TimeDetector.java +33 −4 Original line number Diff line number Diff line Loading @@ -17,19 +17,22 @@ package android.app.timedetector; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.SystemClock; import android.util.Log; import android.util.TimestampedValue; /** * The interface through which system components can send signals to the TimeDetectorService. * @hide */ @SystemService(Context.TIME_DETECTOR_SERVICE) public final class TimeDetector { public class TimeDetector { private static final String TAG = "timedetector.TimeDetector"; private static final boolean DEBUG = false; Loading @@ -41,10 +44,11 @@ public final class TimeDetector { } /** * Suggests the current time to the detector. The detector may ignore the signal if better * signals are available such as those that come from more reliable sources or were * determined more recently. * Suggests the current phone-signal derived time to the detector. The detector may ignore the * signal if better signals are available such as those that come from more reliable sources or * were determined more recently. */ @RequiresPermission(android.Manifest.permission.SET_TIME) public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) { if (DEBUG) { Log.d(TAG, "suggestPhoneTime called: " + timeSuggestion); Loading @@ -56,4 +60,29 @@ public final class TimeDetector { } } /** * Suggests the user's manually entered current time to the detector. */ @RequiresPermission(android.Manifest.permission.SET_TIME) public void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion) { if (DEBUG) { Log.d(TAG, "suggestManualTime called: " + timeSuggestion); } try { mITimeDetectorService.suggestManualTime(timeSuggestion); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * A shared utility method to create a {@link ManualTimeSuggestion}. */ public static ManualTimeSuggestion createManualTimeSuggestion(long when, String why) { TimestampedValue<Long> utcTime = new TimestampedValue<>(SystemClock.elapsedRealtime(), when); ManualTimeSuggestion manualTimeSuggestion = new ManualTimeSuggestion(utcTime); manualTimeSuggestion.addDebugInfo(why); return manualTimeSuggestion; } } services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java +85 −46 Original line number Diff line number Diff line Loading @@ -16,9 +16,11 @@ package com.android.server.timedetector; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; import android.content.Intent; import android.util.Slog; Loading @@ -27,6 +29,8 @@ import android.util.TimestampedValue; import com.android.internal.telephony.TelephonyIntents; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * An implementation of TimeDetectorStrategy that passes only NITZ suggestions to Loading @@ -38,10 +42,22 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { private final static String TAG = "timedetector.SimpleTimeDetectorStrategy"; @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL }) @Retention(RetentionPolicy.SOURCE) public @interface Origin {} /** Used when a time value originated from a telephony signal. */ @Origin private static final int ORIGIN_PHONE = 1; /** Used when a time value originated from a user / manual settings. */ @Origin private static final int ORIGIN_MANUAL = 2; /** * CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the * actual system clock time before a warning is logged. Used to help identify situations where * there is something other than this class setting the system clock. * there is something other than this class setting the system clock automatically. */ private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000; Loading @@ -52,11 +68,11 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { @Nullable private PhoneTimeSuggestion mLastPhoneSuggestion; // Information about the last time signal received: Used when toggling auto-time. @Nullable private TimestampedValue<Long> mLastSystemClockTime; private boolean mLastSystemClockTimeSendNetworkBroadcast; @Nullable private TimestampedValue<Long> mLastAutoSystemClockTime; private boolean mLastAutoSystemClockTimeSendNetworkBroadcast; // System clock state. @Nullable private TimestampedValue<Long> mLastSystemClockTimeSet; @Nullable private TimestampedValue<Long> mLastAutoSystemClockTimeSet; @Override public void initialize(@NonNull Callback callback) { Loading @@ -78,17 +94,18 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { return; } // Always store the last NITZ value received, regardless of whether we go on to use it to // update the system clock. This is so that we can validate future NITZ signals. // update the system clock. This is so that we can validate future phone suggestions. mLastPhoneSuggestion = timeSuggestion; // System clock update logic. final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime(); setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, timeSuggestion); } // Historically, Android has sent a telephony broadcast only when setting the time using // NITZ. final boolean sendNetworkBroadcast = true; @Override public void suggestManualTime(ManualTimeSuggestion timeSuggestion) { final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime(); setSystemClockIfRequired(newUtcTime, sendNetworkBroadcast); setSystemClockIfRequired(ORIGIN_MANUAL, newUtcTime, timeSuggestion); } private static boolean validateNewPhoneSuggestion(@NonNull PhoneTimeSuggestion newSuggestion, Loading @@ -110,54 +127,76 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { } private void setSystemClockIfRequired( TimestampedValue<Long> time, boolean sendNetworkBroadcast) { // Store the last candidate we've seen in all cases so we can set the system clock // when/if time detection is enabled. mLastSystemClockTime = time; mLastSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast; if (!mCallback.isTimeDetectionEnabled()) { Slog.d(TAG, "setSystemClockIfRequired: Time detection is not enabled. time=" + time); @Origin int origin, TimestampedValue<Long> time, Object cause) { // Historically, Android has sent a TelephonyIntents.ACTION_NETWORK_SET_TIME broadcast only // when setting the time using NITZ. boolean sendNetworkBroadcast = origin == ORIGIN_PHONE; boolean isOriginAutomatic = isOriginAutomatic(origin); if (isOriginAutomatic) { // Store the last auto time candidate we've seen in all cases so we can set the system // clock when/if time detection is off but later enabled. mLastAutoSystemClockTime = time; mLastAutoSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast; if (!mCallback.isAutoTimeDetectionEnabled()) { Slog.d(TAG, "setSystemClockIfRequired: Auto time detection is not enabled." + " time=" + time + ", cause=" + cause); return; } } else { if (mCallback.isAutoTimeDetectionEnabled()) { Slog.d(TAG, "setSystemClockIfRequired: Auto time detection is enabled." + " time=" + time + ", cause=" + cause); return; } } mCallback.acquireWakeLock(); try { long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis(); long actualTimeMillis = mCallback.systemClockMillis(); if (isOriginAutomatic) { // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else // may be setting the clock. if (mLastSystemClockTimeSet != null) { if (mLastAutoSystemClockTimeSet != null) { long expectedTimeMillis = TimeDetectorStrategy.getTimeAt( mLastSystemClockTimeSet, elapsedRealtimeMillis); mLastAutoSystemClockTimeSet, elapsedRealtimeMillis); long absSystemClockDifference = Math.abs(expectedTimeMillis - actualTimeMillis); if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) { Slog.w(TAG, "System clock has not tracked elapsed real time clock. A clock may" + " be inaccurate or something unexpectedly set the system clock." Slog.w(TAG, "System clock has not tracked elapsed real time clock. A clock may" + " be inaccurate or something unexpectedly set the system" + " clock." + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " expectedTimeMillis=" + expectedTimeMillis + " actualTimeMillis=" + actualTimeMillis); } } } final String reason = "New time signal"; adjustAndSetDeviceSystemClock( time, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, reason); time, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, cause); } finally { mCallback.releaseWakeLock(); } } private static boolean isOriginAutomatic(@Origin int origin) { return origin == ORIGIN_PHONE; } @Override public void handleAutoTimeDetectionToggle(boolean enabled) { // If automatic time detection is enabled we update the system clock instantly if we can. // Conversely, if automatic time detection is disabled we leave the clock as it is. if (enabled) { if (mLastSystemClockTime != null) { if (mLastAutoSystemClockTime != null) { // Only send the network broadcast if the last candidate would have caused one. final boolean sendNetworkBroadcast = mLastSystemClockTimeSendNetworkBroadcast; final boolean sendNetworkBroadcast = mLastAutoSystemClockTimeSendNetworkBroadcast; mCallback.acquireWakeLock(); try { Loading @@ -165,7 +204,7 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { long actualTimeMillis = mCallback.systemClockMillis(); final String reason = "Automatic time detection enabled."; adjustAndSetDeviceSystemClock(mLastSystemClockTime, sendNetworkBroadcast, adjustAndSetDeviceSystemClock(mLastAutoSystemClockTime, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, reason); } finally { mCallback.releaseWakeLock(); Loading @@ -174,22 +213,22 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { } else { // CLOCK_PARANOIA: We are losing "control" of the system clock so we cannot predict what // it should be in future. mLastSystemClockTimeSet = null; mLastAutoSystemClockTimeSet = null; } } @Override public void dump(@NonNull PrintWriter pw, @Nullable String[] args) { pw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion); pw.println("mLastSystemClockTimeSet=" + mLastSystemClockTimeSet); pw.println("mLastSystemClockTime=" + mLastSystemClockTime); pw.println("mLastSystemClockTimeSendNetworkBroadcast=" + mLastSystemClockTimeSendNetworkBroadcast); pw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet); pw.println("mLastAutoSystemClockTime=" + mLastAutoSystemClockTime); pw.println("mLastAutoSystemClockTimeSendNetworkBroadcast=" + mLastAutoSystemClockTimeSendNetworkBroadcast); } private void adjustAndSetDeviceSystemClock( TimestampedValue<Long> newTime, boolean sendNetworkBroadcast, long elapsedRealtimeMillis, long actualSystemClockMillis, String reason) { long elapsedRealtimeMillis, long actualSystemClockMillis, Object cause) { // Adjust for the time that has elapsed since the signal was received. long newSystemClockMillis = TimeDetectorStrategy.getTimeAt(newTime, elapsedRealtimeMillis); Loading @@ -203,20 +242,20 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { + " system clock are close enough." + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " newTime=" + newTime + " reason=" + reason + " cause=" + cause + " systemClockUpdateThreshold=" + systemClockUpdateThreshold + " absTimeDifference=" + absTimeDifference); return; } Slog.d(TAG, "Setting system clock using time=" + newTime + " reason=" + reason + " cause=" + cause + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " newTimeMillis=" + newSystemClockMillis); mCallback.setSystemClock(newSystemClockMillis); // CLOCK_PARANOIA : Record the last time this class set the system clock. mLastSystemClockTimeSet = newTime; mLastAutoSystemClockTimeSet = newTime; if (sendNetworkBroadcast) { // Send a broadcast that telephony code used to send after setting the clock. Loading Loading
core/java/android/app/timedetector/ITimeDetectorService.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.app.timedetector; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; /** Loading @@ -33,4 +34,5 @@ import android.app.timedetector.PhoneTimeSuggestion; */ interface ITimeDetectorService { void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion); void suggestManualTime(in ManualTimeSuggestion timeSuggestion); }
core/java/android/app/timedetector/ManualTimeSuggestion.aidl 0 → 100644 +19 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app.timedetector; parcelable ManualTimeSuggestion;
core/java/android/app/timedetector/ManualTimeSuggestion.java 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app.timedetector; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.util.TimestampedValue; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; /** * A time signal from a manual (user provided) source. The value consists of the number of * milliseconds elapsed since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime * clock when that number was established. The elapsed realtime clock is considered accurate but * volatile, so time signals must not be persisted across device resets. * * @hide */ public final class ManualTimeSuggestion implements Parcelable { public static final @NonNull Creator<ManualTimeSuggestion> CREATOR = new Creator<ManualTimeSuggestion>() { public ManualTimeSuggestion createFromParcel(Parcel in) { return ManualTimeSuggestion.createFromParcel(in); } public ManualTimeSuggestion[] newArray(int size) { return new ManualTimeSuggestion[size]; } }; @NonNull private final TimestampedValue<Long> mUtcTime; @Nullable private ArrayList<String> mDebugInfo; public ManualTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) { mUtcTime = Objects.requireNonNull(utcTime); } private static ManualTimeSuggestion createFromParcel(Parcel in) { TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); ManualTimeSuggestion suggestion = new ManualTimeSuggestion(utcTime); @SuppressWarnings("unchecked") ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); suggestion.mDebugInfo = debugInfo; return suggestion; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeParcelable(mUtcTime, 0); dest.writeList(mDebugInfo); } @NonNull public TimestampedValue<Long> getUtcTime() { return mUtcTime; } @NonNull public List<String> getDebugInfo() { return Collections.unmodifiableList(mDebugInfo); } /** * Associates information with the instance that can be useful for debugging / logging. The * information is present in {@link #toString()} but is not considered for * {@link #equals(Object)} and {@link #hashCode()}. */ public void addDebugInfo(String... debugInfos) { if (mDebugInfo == null) { mDebugInfo = new ArrayList<>(); } mDebugInfo.addAll(Arrays.asList(debugInfos)); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ManualTimeSuggestion that = (ManualTimeSuggestion) o; return Objects.equals(mUtcTime, that.mUtcTime); } @Override public int hashCode() { return Objects.hash(mUtcTime); } @Override public String toString() { return "ManualTimeSuggestion{" + "mUtcTime=" + mUtcTime + ", mDebugInfo=" + mDebugInfo + '}'; } }
core/java/android/app/timedetector/TimeDetector.java +33 −4 Original line number Diff line number Diff line Loading @@ -17,19 +17,22 @@ package android.app.timedetector; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.SystemClock; import android.util.Log; import android.util.TimestampedValue; /** * The interface through which system components can send signals to the TimeDetectorService. * @hide */ @SystemService(Context.TIME_DETECTOR_SERVICE) public final class TimeDetector { public class TimeDetector { private static final String TAG = "timedetector.TimeDetector"; private static final boolean DEBUG = false; Loading @@ -41,10 +44,11 @@ public final class TimeDetector { } /** * Suggests the current time to the detector. The detector may ignore the signal if better * signals are available such as those that come from more reliable sources or were * determined more recently. * Suggests the current phone-signal derived time to the detector. The detector may ignore the * signal if better signals are available such as those that come from more reliable sources or * were determined more recently. */ @RequiresPermission(android.Manifest.permission.SET_TIME) public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) { if (DEBUG) { Log.d(TAG, "suggestPhoneTime called: " + timeSuggestion); Loading @@ -56,4 +60,29 @@ public final class TimeDetector { } } /** * Suggests the user's manually entered current time to the detector. */ @RequiresPermission(android.Manifest.permission.SET_TIME) public void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion) { if (DEBUG) { Log.d(TAG, "suggestManualTime called: " + timeSuggestion); } try { mITimeDetectorService.suggestManualTime(timeSuggestion); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * A shared utility method to create a {@link ManualTimeSuggestion}. */ public static ManualTimeSuggestion createManualTimeSuggestion(long when, String why) { TimestampedValue<Long> utcTime = new TimestampedValue<>(SystemClock.elapsedRealtime(), when); ManualTimeSuggestion manualTimeSuggestion = new ManualTimeSuggestion(utcTime); manualTimeSuggestion.addDebugInfo(why); return manualTimeSuggestion; } }
services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java +85 −46 Original line number Diff line number Diff line Loading @@ -16,9 +16,11 @@ package com.android.server.timedetector; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; import android.content.Intent; import android.util.Slog; Loading @@ -27,6 +29,8 @@ import android.util.TimestampedValue; import com.android.internal.telephony.TelephonyIntents; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * An implementation of TimeDetectorStrategy that passes only NITZ suggestions to Loading @@ -38,10 +42,22 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { private final static String TAG = "timedetector.SimpleTimeDetectorStrategy"; @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL }) @Retention(RetentionPolicy.SOURCE) public @interface Origin {} /** Used when a time value originated from a telephony signal. */ @Origin private static final int ORIGIN_PHONE = 1; /** Used when a time value originated from a user / manual settings. */ @Origin private static final int ORIGIN_MANUAL = 2; /** * CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the * actual system clock time before a warning is logged. Used to help identify situations where * there is something other than this class setting the system clock. * there is something other than this class setting the system clock automatically. */ private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000; Loading @@ -52,11 +68,11 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { @Nullable private PhoneTimeSuggestion mLastPhoneSuggestion; // Information about the last time signal received: Used when toggling auto-time. @Nullable private TimestampedValue<Long> mLastSystemClockTime; private boolean mLastSystemClockTimeSendNetworkBroadcast; @Nullable private TimestampedValue<Long> mLastAutoSystemClockTime; private boolean mLastAutoSystemClockTimeSendNetworkBroadcast; // System clock state. @Nullable private TimestampedValue<Long> mLastSystemClockTimeSet; @Nullable private TimestampedValue<Long> mLastAutoSystemClockTimeSet; @Override public void initialize(@NonNull Callback callback) { Loading @@ -78,17 +94,18 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { return; } // Always store the last NITZ value received, regardless of whether we go on to use it to // update the system clock. This is so that we can validate future NITZ signals. // update the system clock. This is so that we can validate future phone suggestions. mLastPhoneSuggestion = timeSuggestion; // System clock update logic. final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime(); setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, timeSuggestion); } // Historically, Android has sent a telephony broadcast only when setting the time using // NITZ. final boolean sendNetworkBroadcast = true; @Override public void suggestManualTime(ManualTimeSuggestion timeSuggestion) { final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime(); setSystemClockIfRequired(newUtcTime, sendNetworkBroadcast); setSystemClockIfRequired(ORIGIN_MANUAL, newUtcTime, timeSuggestion); } private static boolean validateNewPhoneSuggestion(@NonNull PhoneTimeSuggestion newSuggestion, Loading @@ -110,54 +127,76 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { } private void setSystemClockIfRequired( TimestampedValue<Long> time, boolean sendNetworkBroadcast) { // Store the last candidate we've seen in all cases so we can set the system clock // when/if time detection is enabled. mLastSystemClockTime = time; mLastSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast; if (!mCallback.isTimeDetectionEnabled()) { Slog.d(TAG, "setSystemClockIfRequired: Time detection is not enabled. time=" + time); @Origin int origin, TimestampedValue<Long> time, Object cause) { // Historically, Android has sent a TelephonyIntents.ACTION_NETWORK_SET_TIME broadcast only // when setting the time using NITZ. boolean sendNetworkBroadcast = origin == ORIGIN_PHONE; boolean isOriginAutomatic = isOriginAutomatic(origin); if (isOriginAutomatic) { // Store the last auto time candidate we've seen in all cases so we can set the system // clock when/if time detection is off but later enabled. mLastAutoSystemClockTime = time; mLastAutoSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast; if (!mCallback.isAutoTimeDetectionEnabled()) { Slog.d(TAG, "setSystemClockIfRequired: Auto time detection is not enabled." + " time=" + time + ", cause=" + cause); return; } } else { if (mCallback.isAutoTimeDetectionEnabled()) { Slog.d(TAG, "setSystemClockIfRequired: Auto time detection is enabled." + " time=" + time + ", cause=" + cause); return; } } mCallback.acquireWakeLock(); try { long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis(); long actualTimeMillis = mCallback.systemClockMillis(); if (isOriginAutomatic) { // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else // may be setting the clock. if (mLastSystemClockTimeSet != null) { if (mLastAutoSystemClockTimeSet != null) { long expectedTimeMillis = TimeDetectorStrategy.getTimeAt( mLastSystemClockTimeSet, elapsedRealtimeMillis); mLastAutoSystemClockTimeSet, elapsedRealtimeMillis); long absSystemClockDifference = Math.abs(expectedTimeMillis - actualTimeMillis); if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) { Slog.w(TAG, "System clock has not tracked elapsed real time clock. A clock may" + " be inaccurate or something unexpectedly set the system clock." Slog.w(TAG, "System clock has not tracked elapsed real time clock. A clock may" + " be inaccurate or something unexpectedly set the system" + " clock." + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " expectedTimeMillis=" + expectedTimeMillis + " actualTimeMillis=" + actualTimeMillis); } } } final String reason = "New time signal"; adjustAndSetDeviceSystemClock( time, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, reason); time, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, cause); } finally { mCallback.releaseWakeLock(); } } private static boolean isOriginAutomatic(@Origin int origin) { return origin == ORIGIN_PHONE; } @Override public void handleAutoTimeDetectionToggle(boolean enabled) { // If automatic time detection is enabled we update the system clock instantly if we can. // Conversely, if automatic time detection is disabled we leave the clock as it is. if (enabled) { if (mLastSystemClockTime != null) { if (mLastAutoSystemClockTime != null) { // Only send the network broadcast if the last candidate would have caused one. final boolean sendNetworkBroadcast = mLastSystemClockTimeSendNetworkBroadcast; final boolean sendNetworkBroadcast = mLastAutoSystemClockTimeSendNetworkBroadcast; mCallback.acquireWakeLock(); try { Loading @@ -165,7 +204,7 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { long actualTimeMillis = mCallback.systemClockMillis(); final String reason = "Automatic time detection enabled."; adjustAndSetDeviceSystemClock(mLastSystemClockTime, sendNetworkBroadcast, adjustAndSetDeviceSystemClock(mLastAutoSystemClockTime, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, reason); } finally { mCallback.releaseWakeLock(); Loading @@ -174,22 +213,22 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { } else { // CLOCK_PARANOIA: We are losing "control" of the system clock so we cannot predict what // it should be in future. mLastSystemClockTimeSet = null; mLastAutoSystemClockTimeSet = null; } } @Override public void dump(@NonNull PrintWriter pw, @Nullable String[] args) { pw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion); pw.println("mLastSystemClockTimeSet=" + mLastSystemClockTimeSet); pw.println("mLastSystemClockTime=" + mLastSystemClockTime); pw.println("mLastSystemClockTimeSendNetworkBroadcast=" + mLastSystemClockTimeSendNetworkBroadcast); pw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet); pw.println("mLastAutoSystemClockTime=" + mLastAutoSystemClockTime); pw.println("mLastAutoSystemClockTimeSendNetworkBroadcast=" + mLastAutoSystemClockTimeSendNetworkBroadcast); } private void adjustAndSetDeviceSystemClock( TimestampedValue<Long> newTime, boolean sendNetworkBroadcast, long elapsedRealtimeMillis, long actualSystemClockMillis, String reason) { long elapsedRealtimeMillis, long actualSystemClockMillis, Object cause) { // Adjust for the time that has elapsed since the signal was received. long newSystemClockMillis = TimeDetectorStrategy.getTimeAt(newTime, elapsedRealtimeMillis); Loading @@ -203,20 +242,20 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { + " system clock are close enough." + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " newTime=" + newTime + " reason=" + reason + " cause=" + cause + " systemClockUpdateThreshold=" + systemClockUpdateThreshold + " absTimeDifference=" + absTimeDifference); return; } Slog.d(TAG, "Setting system clock using time=" + newTime + " reason=" + reason + " cause=" + cause + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + " newTimeMillis=" + newSystemClockMillis); mCallback.setSystemClock(newSystemClockMillis); // CLOCK_PARANOIA : Record the last time this class set the system clock. mLastSystemClockTimeSet = newTime; mLastAutoSystemClockTimeSet = newTime; if (sendNetworkBroadcast) { // Send a broadcast that telephony code used to send after setting the clock. Loading