diff options
| author | 2019-11-28 00:54:46 -0800 | |
|---|---|---|
| committer | 2019-11-28 00:54:46 -0800 | |
| commit | b07f90adb8d88cb84b5d52d39eace93594846865 (patch) | |
| tree | c87e95a1a2f3db85f598976d6bc36677299bddf9 | |
| parent | 0a1aca082c36e997477eb2d7cd6cef1cd9179597 (diff) | |
| parent | 5f01cb6ddca16e231c9ce57baa6949c075b3cc05 (diff) | |
Merge "Add a new method to set time"
am: 5f01cb6ddc
Change-Id: If92a40f84971a663f99d7d764cd45e8d3510bfda
15 files changed, 504 insertions, 77 deletions
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl index ddc4932d6fec..9877fc741b7b 100644 --- a/core/java/android/app/timedetector/ITimeDetectorService.aidl +++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl @@ -16,6 +16,7 @@ package android.app.timedetector; +import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; /** @@ -33,4 +34,5 @@ import android.app.timedetector.PhoneTimeSuggestion; */ interface ITimeDetectorService { void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion); + void suggestManualTime(in ManualTimeSuggestion timeSuggestion); } diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.aidl b/core/java/android/app/timedetector/ManualTimeSuggestion.aidl new file mode 100644 index 000000000000..213940493114 --- /dev/null +++ b/core/java/android/app/timedetector/ManualTimeSuggestion.aidl @@ -0,0 +1,19 @@ +/* + * 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; diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java new file mode 100644 index 000000000000..e7d619a27607 --- /dev/null +++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java @@ -0,0 +1,127 @@ +/* + * 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 + + '}'; + } +} diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java index 334e9582a145..48d5cd2d65a5 100644 --- a/core/java/android/app/timedetector/TimeDetector.java +++ b/core/java/android/app/timedetector/TimeDetector.java @@ -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; @@ -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); @@ -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; + } } diff --git a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java index fd8cc24b658b..3c79b2399fa3 100644 --- a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java @@ -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; @@ -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 @@ -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; @@ -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) { @@ -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, @@ -110,16 +127,31 @@ 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); - return; + @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(); @@ -127,37 +159,44 @@ public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy { long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis(); long actualTimeMillis = mCallback.systemClockMillis(); - // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else - // may be setting the clock. - if (mLastSystemClockTimeSet != null) { - long expectedTimeMillis = TimeDetectorStrategy.getTimeAt( - mLastSystemClockTimeSet, 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." - + " elapsedRealtimeMillis=" + elapsedRealtimeMillis - + " expectedTimeMillis=" + expectedTimeMillis - + " actualTimeMillis=" + actualTimeMillis); + if (isOriginAutomatic) { + // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else + // may be setting the clock. + if (mLastAutoSystemClockTimeSet != null) { + long expectedTimeMillis = TimeDetectorStrategy.getTimeAt( + 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." + + " 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 { @@ -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(); @@ -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); @@ -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. diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index ee42279f7d50..09309751d493 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java @@ -19,6 +19,7 @@ package com.android.server.timedetector; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.timedetector.ITimeDetectorService; +import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; import android.content.ContentResolver; import android.content.Context; @@ -97,7 +98,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @Override public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSignal) { - enforceSetTimePermission(); + enforceSuggestPhoneTimePermission(); Objects.requireNonNull(timeSignal); long idToken = Binder.clearCallingIdentity(); @@ -110,10 +111,25 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { } } + @Override + public void suggestManualTime(@NonNull ManualTimeSuggestion timeSignal) { + enforceSuggestManualTimePermission(); + Objects.requireNonNull(timeSignal); + + long idToken = Binder.clearCallingIdentity(); + try { + synchronized (mStrategyLock) { + mTimeDetectorStrategy.suggestManualTime(timeSignal); + } + } finally { + Binder.restoreCallingIdentity(idToken); + } + } + @VisibleForTesting public void handleAutoTimeDetectionToggle() { synchronized (mStrategyLock) { - final boolean timeDetectionEnabled = mCallback.isTimeDetectionEnabled(); + final boolean timeDetectionEnabled = mCallback.isAutoTimeDetectionEnabled(); mTimeDetectorStrategy.handleAutoTimeDetectionToggle(timeDetectionEnabled); } } @@ -128,7 +144,11 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { } } - private void enforceSetTimePermission() { + private void enforceSuggestPhoneTimePermission() { + mContext.enforceCallingPermission(android.Manifest.permission.SET_TIME, "set time"); + } + + private void enforceSuggestManualTimePermission() { mContext.enforceCallingPermission(android.Manifest.permission.SET_TIME, "set time"); } -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index 7c2a945854f5..b60cebf57b45 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -18,6 +18,7 @@ package com.android.server.timedetector; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; import android.content.Intent; import android.util.TimestampedValue; @@ -47,7 +48,7 @@ public interface TimeDetectorStrategy { int systemClockUpdateThresholdMillis(); /** Returns true if automatic time detection is enabled. */ - boolean isTimeDetectionEnabled(); + boolean isAutoTimeDetectionEnabled(); /** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */ void acquireWakeLock(); @@ -71,9 +72,12 @@ public interface TimeDetectorStrategy { /** Initialize the strategy. */ void initialize(@NonNull Callback callback); - /** Process the suggested time. */ + /** Process the suggested time from telephony sources. */ void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion); + /** Process the suggested manually entered time. */ + void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion); + /** Handle the auto-time setting being toggled on or off. */ void handleAutoTimeDetectionToggle(boolean enabled); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java index 77b9e6281086..42d59d51c6af 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java @@ -72,7 +72,7 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat } @Override - public boolean isTimeDetectionEnabled() { + public boolean isAutoTimeDetectionEnabled() { try { return Settings.Global.getInt(mContentResolver, Settings.Global.AUTO_TIME) != 0; } catch (Settings.SettingNotFoundException snfe) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 2e56fb096e3b..e8617bce920e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -133,6 +133,8 @@ import android.app.admin.StartInstallingUpdateCallback; import android.app.admin.SystemUpdateInfo; import android.app.admin.SystemUpdatePolicy; import android.app.backup.IBackupManager; +import android.app.timedetector.ManualTimeSuggestion; +import android.app.timedetector.TimeDetector; import android.app.trust.TrustManager; import android.app.usage.UsageStatsManagerInternal; import android.content.ActivityNotFoundException; @@ -1950,6 +1952,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mContext.getSystemService(AlarmManager.class); } + TimeDetector getTimeDetector() { + return mContext.getSystemService(TimeDetector.class); + } + ConnectivityManager getConnectivityManager() { return mContext.getSystemService(ConnectivityManager.class); } @@ -10859,7 +10865,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) { return false; } - mInjector.binderWithCleanCallingIdentity(() -> mInjector.getAlarmManager().setTime(millis)); + ManualTimeSuggestion manualTimeSuggestion = TimeDetector.createManualTimeSuggestion( + millis, "DevicePolicyManagerService: setTime"); + mInjector.binderWithCleanCallingIdentity( + () -> mInjector.getTimeDetector().suggestManualTime(manualTimeSuggestion)); return true; } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index 2ce4c54a932b..0fde850d3021 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -22,6 +22,7 @@ import android.app.IActivityTaskManager; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.backup.IBackupManager; +import android.app.timedetector.TimeDetector; import android.app.usage.UsageStatsManagerInternal; import android.content.Context; import android.content.Intent; @@ -217,6 +218,11 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi AlarmManager getAlarmManager() {return services.alarmManager;} @Override + TimeDetector getTimeDetector() { + return services.timeDetector; + } + + @Override LockPatternUtils newLockPatternUtils() { return services.lockPatternUtils; } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 9ae9824da3e2..b93b47ad74bd 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -63,6 +63,7 @@ import android.app.admin.DeviceAdminReceiver; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; import android.app.admin.PasswordMetrics; +import android.app.timedetector.ManualTimeSuggestion; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Intent; @@ -3473,7 +3474,19 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); dpm.setTime(admin1, 0); - verify(getServices().alarmManager).setTime(0); + + BaseMatcher<ManualTimeSuggestion> hasZeroTime = new BaseMatcher<ManualTimeSuggestion>() { + @Override + public boolean matches(Object item) { + final ManualTimeSuggestion suggestion = (ManualTimeSuggestion) item; + return suggestion.getUtcTime().getValue() == 0; + } + @Override + public void describeTo(Description description) { + description.appendText("ManualTimeSuggestion{utcTime.value=0}"); + } + }; + verify(getServices().timeDetector).suggestManualTime(argThat(hasZeroTime)); } public void testSetTimeFailWithPO() throws Exception { diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java index bd513dc083be..1a67576c218f 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -207,6 +207,8 @@ public class DpmMockContext extends MockContext { switch (name) { case Context.ALARM_SERVICE: return mMockSystemServices.alarmManager; + case Context.TIME_DETECTOR_SERVICE: + return mMockSystemServices.timeDetector; case Context.USER_SERVICE: return mMockSystemServices.userManager; case Context.POWER_SERVICE: diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index 8f0aeea3dbf8..b208f828ee85 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -31,6 +31,7 @@ import android.app.IActivityManager; import android.app.IActivityTaskManager; import android.app.NotificationManager; import android.app.backup.IBackupManager; +import android.app.timedetector.TimeDetector; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.ContentValues; @@ -107,6 +108,7 @@ public class MockSystemServices { public final TelephonyManager telephonyManager; public final AccountManager accountManager; public final AlarmManager alarmManager; + public final TimeDetector timeDetector; public final KeyChain.KeyChainConnection keyChainConnection; /** Note this is a partial mock, not a real mock. */ public final PackageManager packageManager; @@ -146,6 +148,7 @@ public class MockSystemServices { telephonyManager = mock(TelephonyManager.class); accountManager = mock(AccountManager.class); alarmManager = mock(AlarmManager.class); + timeDetector = mock(TimeDetector.class); keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS); // Package manager is huge, so we use a partial mock instead. diff --git a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java index b49845ae4006..317fd4d566ae 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeZoneDetectorStrategyTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; import android.content.Intent; import android.icu.util.Calendar; @@ -36,6 +37,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.time.Duration; + @RunWith(AndroidJUnit4.class) public class SimpleTimeZoneDetectorStrategyTest { @@ -47,6 +50,8 @@ public class SimpleTimeZoneDetectorStrategyTest { private static final int ARBITRARY_PHONE_ID = 123456; + private static final long ONE_DAY_MILLIS = Duration.ofDays(1).toMillis(); + private Script mScript; @Before @@ -55,7 +60,7 @@ public class SimpleTimeZoneDetectorStrategyTest { } @Test - public void testSuggestPhoneTime_nitz_timeDetectionEnabled() { + public void testSuggestPhoneTime_autoTimeEnabled() { Scenario scenario = SCENARIO_1; mScript.pokeFakeClocks(scenario) .pokeTimeDetectionEnabled(true); @@ -67,7 +72,8 @@ public class SimpleTimeZoneDetectorStrategyTest { mScript.simulateTimePassing(clockIncrement) .simulatePhoneTimeSuggestion(timeSuggestion) - .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis); + .verifySystemClockWasSetAndResetCallTracking( + expectSystemClockMillis, true /* expectNetworkBroadcast */); } @Test @@ -103,7 +109,8 @@ public class SimpleTimeZoneDetectorStrategyTest { // Send the first time signal. It should be used. mScript.simulatePhoneTimeSuggestion(timeSuggestion1) - .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis1); + .verifySystemClockWasSetAndResetCallTracking( + expectSystemClockMillis1, true /* expectNetworkBroadcast */); // Now send another time signal, but one that is too similar to the last one and should be // ignored. @@ -130,11 +137,12 @@ public class SimpleTimeZoneDetectorStrategyTest { TimeDetectorStrategy.getTimeAt(utcTime3, mScript.peekElapsedRealtimeMillis()); mScript.simulatePhoneTimeSuggestion(timeSuggestion3) - .verifySystemClockWasSetAndResetCallTracking(expectSystemClockMillis3); + .verifySystemClockWasSetAndResetCallTracking( + expectSystemClockMillis3, true /* expectNetworkBroadcast */); } @Test - public void testSuggestPhoneTime_nitz_timeDetectionDisabled() { + public void testSuggestPhoneTime_autoTimeDisabled() { Scenario scenario = SCENARIO_1; mScript.pokeFakeClocks(scenario) .pokeTimeDetectionEnabled(false); @@ -146,7 +154,7 @@ public class SimpleTimeZoneDetectorStrategyTest { } @Test - public void testSuggestPhoneTime_nitz_invalidNitzReferenceTimesIgnored() { + public void testSuggestPhoneTime_invalidNitzReferenceTimesIgnored() { Scenario scenario = SCENARIO_1; final int systemClockUpdateThreshold = 2000; mScript.pokeFakeClocks(scenario) @@ -161,7 +169,8 @@ public class SimpleTimeZoneDetectorStrategyTest { long expectedSystemClockMillis1 = TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis()); mScript.simulatePhoneTimeSuggestion(timeSuggestion1) - .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1); + .verifySystemClockWasSetAndResetCallTracking( + expectedSystemClockMillis1, true /* expectNetworkBroadcast */); // The UTC time increment should be larger than the system clock update threshold so we // know it shouldn't be ignored for other reasons. @@ -197,7 +206,8 @@ public class SimpleTimeZoneDetectorStrategyTest { PhoneTimeSuggestion timeSuggestion4 = createPhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime4); mScript.simulatePhoneTimeSuggestion(timeSuggestion4) - .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4); + .verifySystemClockWasSetAndResetCallTracking( + expectedSystemClockMillis4, true /* expectNetworkBroadcast */); } @Test @@ -229,7 +239,8 @@ public class SimpleTimeZoneDetectorStrategyTest { // Turn on auto time detection. mScript.simulateAutoTimeDetectionToggle() - .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1); + .verifySystemClockWasSetAndResetCallTracking( + expectedSystemClockMillis1, true /* expectNetworkBroadcast */); // Turn off auto time detection. mScript.simulateAutoTimeDetectionToggle() @@ -256,7 +267,99 @@ public class SimpleTimeZoneDetectorStrategyTest { // Turn on auto time detection. mScript.simulateAutoTimeDetectionToggle() - .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis2); + .verifySystemClockWasSetAndResetCallTracking( + expectedSystemClockMillis2, true /* expectNetworkBroadcast */); + } + + @Test + public void testSuggestManualTime_autoTimeDisabled() { + Scenario scenario = SCENARIO_1; + mScript.pokeFakeClocks(scenario) + .pokeTimeDetectionEnabled(false); + + ManualTimeSuggestion timeSuggestion = scenario.createManualTimeSuggestionForActual(); + final int clockIncrement = 1000; + long expectSystemClockMillis = scenario.getActualTimeMillis() + clockIncrement; + + mScript.simulateTimePassing(clockIncrement) + .simulateManualTimeSuggestion(timeSuggestion) + .verifySystemClockWasSetAndResetCallTracking( + expectSystemClockMillis, false /* expectNetworkBroadcast */); + } + + @Test + public void testSuggestManualTime_retainsAutoSignal() { + Scenario scenario = SCENARIO_1; + + // Configure the start state. + mScript.pokeFakeClocks(scenario) + .pokeTimeDetectionEnabled(true); + + // Simulate a phone suggestion. + PhoneTimeSuggestion phoneTimeSuggestion = + scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID); + long expectedAutoClockMillis = phoneTimeSuggestion.getUtcTime().getValue(); + final int clockIncrement = 1000; + + // Simulate the passage of time. + mScript.simulateTimePassing(clockIncrement); + expectedAutoClockMillis += clockIncrement; + + mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion) + .verifySystemClockWasSetAndResetCallTracking( + expectedAutoClockMillis, true /* expectNetworkBroadcast */); + + // Simulate the passage of time. + mScript.simulateTimePassing(clockIncrement); + expectedAutoClockMillis += clockIncrement; + + // Switch to manual. + mScript.simulateAutoTimeDetectionToggle() + .verifySystemClockWasNotSetAndResetCallTracking(); + + // Simulate the passage of time. + mScript.simulateTimePassing(clockIncrement); + expectedAutoClockMillis += clockIncrement; + + + // Simulate a manual suggestion 1 day different from the auto suggestion. + long manualTimeMillis = SCENARIO_1.getActualTimeMillis() + ONE_DAY_MILLIS; + long expectedManualClockMillis = manualTimeMillis; + ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion(manualTimeMillis); + mScript.simulateManualTimeSuggestion(manualTimeSuggestion) + .verifySystemClockWasSetAndResetCallTracking( + expectedManualClockMillis, false /* expectNetworkBroadcast */); + + // Simulate the passage of time. + mScript.simulateTimePassing(clockIncrement); + expectedAutoClockMillis += clockIncrement; + + // Switch back to auto. + mScript.simulateAutoTimeDetectionToggle(); + + mScript.verifySystemClockWasSetAndResetCallTracking( + expectedAutoClockMillis, true /* expectNetworkBroadcast */); + + // Switch back to manual - nothing should happen to the clock. + mScript.simulateAutoTimeDetectionToggle() + .verifySystemClockWasNotSetAndResetCallTracking(); + } + + /** + * Manual suggestions should be ignored if auto time is enabled. + */ + @Test + public void testSuggestManualTime_autoTimeEnabled() { + Scenario scenario = SCENARIO_1; + mScript.pokeFakeClocks(scenario) + .pokeTimeDetectionEnabled(true); + + ManualTimeSuggestion timeSuggestion = scenario.createManualTimeSuggestionForActual(); + final int clockIncrement = 1000; + + mScript.simulateTimePassing(clockIncrement) + .simulateManualTimeSuggestion(timeSuggestion) + .verifySystemClockWasNotSetAndResetCallTracking(); } /** @@ -280,7 +383,7 @@ public class SimpleTimeZoneDetectorStrategyTest { } @Override - public boolean isTimeDetectionEnabled() { + public boolean isAutoTimeDetectionEnabled() { return mTimeDetectionEnabled; } @@ -426,8 +529,13 @@ public class SimpleTimeZoneDetectorStrategyTest { return this; } + Script simulateManualTimeSuggestion(ManualTimeSuggestion timeSuggestion) { + mSimpleTimeDetectorStrategy.suggestManualTime(timeSuggestion); + return this; + } + Script simulateAutoTimeDetectionToggle() { - boolean enabled = !mFakeCallback.isTimeDetectionEnabled(); + boolean enabled = !mFakeCallback.isAutoTimeDetectionEnabled(); mFakeCallback.pokeTimeDetectionEnabled(enabled); mSimpleTimeDetectorStrategy.handleAutoTimeDetectionToggle(enabled); return this; @@ -445,9 +553,12 @@ public class SimpleTimeZoneDetectorStrategyTest { return this; } - Script verifySystemClockWasSetAndResetCallTracking(long expectSystemClockMillis) { + Script verifySystemClockWasSetAndResetCallTracking( + long expectSystemClockMillis, boolean expectNetworkBroadcast) { mFakeCallback.verifySystemClockWasSet(expectSystemClockMillis); - mFakeCallback.verifyIntentWasBroadcast(); + if (expectNetworkBroadcast) { + mFakeCallback.verifyIntentWasBroadcast(); + } mFakeCallback.resetCallTracking(); return this; } @@ -486,6 +597,12 @@ public class SimpleTimeZoneDetectorStrategyTest { return createPhoneTimeSuggestion(phoneId, time); } + ManualTimeSuggestion createManualTimeSuggestionForActual() { + TimestampedValue<Long> time = new TimestampedValue<>( + mInitialDeviceRealtimeMillis, mActualTimeMillis); + return new ManualTimeSuggestion(time); + } + static class Builder { private long mInitialDeviceSystemClockMillis; @@ -525,6 +642,12 @@ public class SimpleTimeZoneDetectorStrategyTest { return timeSuggestion; } + private ManualTimeSuggestion createManualTimeSuggestion(long timeMillis) { + TimestampedValue<Long> utcTime = + new TimestampedValue<>(mScript.peekElapsedRealtimeMillis(), timeMillis); + return new ManualTimeSuggestion(utcTime); + } + private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute, int second) { Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC")); diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index cfd8a457d2f0..4efe771a4e95 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.PhoneTimeSuggestion; import android.content.Context; import android.content.pm.PackageManager; @@ -90,6 +91,19 @@ public class TimeDetectorServiceTest { } @Test + public void testSuggestManualTime() { + doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); + + ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion(); + mTimeDetectorService.suggestManualTime(manualTimeSuggestion); + + verify(mMockContext).enforceCallingPermission( + eq(android.Manifest.permission.SET_TIME), + anyString()); + mStubbedTimeDetectorStrategy.verifySuggestManualTimeCalled(manualTimeSuggestion); + } + + @Test public void testDump() { when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)) .thenReturn(PackageManager.PERMISSION_GRANTED); @@ -102,13 +116,13 @@ public class TimeDetectorServiceTest { @Test public void testAutoTimeDetectionToggle() { - when(mMockCallback.isTimeDetectionEnabled()).thenReturn(true); + when(mMockCallback.isAutoTimeDetectionEnabled()).thenReturn(true); mTimeDetectorService.handleAutoTimeDetectionToggle(); mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionToggleCalled(true); - when(mMockCallback.isTimeDetectionEnabled()).thenReturn(false); + when(mMockCallback.isAutoTimeDetectionEnabled()).thenReturn(false); mTimeDetectorService.handleAutoTimeDetectionToggle(); @@ -123,10 +137,16 @@ public class TimeDetectorServiceTest { return suggestion; } + private static ManualTimeSuggestion createManualTimeSuggestion() { + TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L); + return new ManualTimeSuggestion(timeValue); + } + private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy { // Call tracking. private PhoneTimeSuggestion mLastPhoneSuggestion; + private ManualTimeSuggestion mLastManualSuggestion; private Boolean mLastAutoTimeDetectionToggle; private boolean mDumpCalled; @@ -141,6 +161,12 @@ public class TimeDetectorServiceTest { } @Override + public void suggestManualTime(ManualTimeSuggestion timeSuggestion) { + resetCallTracking(); + mLastManualSuggestion = timeSuggestion; + } + + @Override public void handleAutoTimeDetectionToggle(boolean enabled) { resetCallTracking(); mLastAutoTimeDetectionToggle = enabled; @@ -154,12 +180,17 @@ public class TimeDetectorServiceTest { void resetCallTracking() { mLastPhoneSuggestion = null; + mLastManualSuggestion = null; mLastAutoTimeDetectionToggle = null; mDumpCalled = false; } - void verifySuggestPhoneTimeCalled(PhoneTimeSuggestion expectedSignal) { - assertEquals(expectedSignal, mLastPhoneSuggestion); + void verifySuggestPhoneTimeCalled(PhoneTimeSuggestion expectedSuggestion) { + assertEquals(expectedSuggestion, mLastPhoneSuggestion); + } + + public void verifySuggestManualTimeCalled(ManualTimeSuggestion expectedSuggestion) { + assertEquals(expectedSuggestion, mLastManualSuggestion); } void verifyHandleAutoTimeDetectionToggleCalled(boolean expectedEnable) { |