Merge "Add a new method to set time"

am: 5f01cb6ddc

Change-Id: If92a40f84971a663f99d7d764cd45e8d3510bfda
This commit is contained in:
Neil Fuller
2019-11-28 00:54:46 -08:00
committed by android-build-merger
15 changed files with 503 additions and 76 deletions

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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
+ '}';
}
}

View File

@@ -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;
}
}

View File

@@ -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.
// Historically, Android has sent a telephony broadcast only when setting the time using
// NITZ.
final boolean sendNetworkBroadcast = true;
final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
setSystemClockIfRequired(newUtcTime, sendNetworkBroadcast);
setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, timeSuggestion);
}
@Override
public void suggestManualTime(ManualTimeSuggestion timeSuggestion) {
final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
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) {
@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;
// 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;
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.isTimeDetectionEnabled()) {
Slog.d(TAG, "setSystemClockIfRequired: Time detection is not enabled. time=" + time);
return;
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.

View File

@@ -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");
}
}

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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;
@@ -216,6 +217,11 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
@Override
AlarmManager getAlarmManager() {return services.alarmManager;}
@Override
TimeDetector getTimeDetector() {
return services.timeDetector;
}
@Override
LockPatternUtils newLockPatternUtils() {
return services.lockPatternUtils;

View File

@@ -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 {

View File

@@ -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:

View File

@@ -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.

View File

@@ -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"));

View File

@@ -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;
@@ -89,6 +90,19 @@ public class TimeDetectorServiceTest {
mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
}
@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))
@@ -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;
@@ -140,6 +160,12 @@ public class TimeDetectorServiceTest {
mLastPhoneSuggestion = timeSuggestion;
}
@Override
public void suggestManualTime(ManualTimeSuggestion timeSuggestion) {
resetCallTracking();
mLastManualSuggestion = timeSuggestion;
}
@Override
public void handleAutoTimeDetectionToggle(boolean enabled) {
resetCallTracking();
@@ -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) {