diff options
30 files changed, 938 insertions, 495 deletions
diff --git a/services/core/java/com/android/server/timedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java index d9a4266e2812..4f221b532b75 100644 --- a/services/core/java/com/android/server/timedetector/ConfigurationInternal.java +++ b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java @@ -155,21 +155,33 @@ public final class ConfigurationInternal { return UserHandle.of(mUserId); } - /** Returns true if the user allowed to modify time zone configuration. */ + /** + * Returns true if the user is allowed to modify time configuration, e.g. can be false due + * to device policy (enterprise). + * + * <p>See also {@link #createCapabilitiesAndConfig(boolean)} for situations where this + * value are ignored. + */ public boolean isUserConfigAllowed() { return mUserConfigAllowed; } - /** Returns a {@link TimeCapabilitiesAndConfig} objects based on configuration values. */ - public TimeCapabilitiesAndConfig capabilitiesAndConfig() { - return new TimeCapabilitiesAndConfig(timeCapabilities(), timeConfiguration()); + /** + * Returns a {@link TimeCapabilitiesAndConfig} objects based on configuration values. + * + * @param bypassUserPolicyChecks {@code true} for device policy manager use cases where device + * policy restrictions that should apply to actual users can be ignored + */ + public TimeCapabilitiesAndConfig createCapabilitiesAndConfig(boolean bypassUserPolicyChecks) { + return new TimeCapabilitiesAndConfig( + timeCapabilities(bypassUserPolicyChecks), timeConfiguration()); } - private TimeCapabilities timeCapabilities() { + private TimeCapabilities timeCapabilities(boolean bypassUserPolicyChecks) { UserHandle userHandle = UserHandle.of(mUserId); TimeCapabilities.Builder builder = new TimeCapabilities.Builder(userHandle); - boolean allowConfigDateTime = isUserConfigAllowed(); + boolean allowConfigDateTime = isUserConfigAllowed() || bypassUserPolicyChecks; boolean deviceHasAutoTimeDetection = isAutoDetectionSupported(); final @CapabilityState int configureAutoDetectionEnabledCapability; @@ -186,15 +198,15 @@ public final class ConfigurationInternal { // current logic above, this could lead to a situation where a device hardware does not // support auto detection, the device has been forced into "auto" mode by an admin and the // user is unable to disable auto detection. - final @CapabilityState int suggestManualTimeZoneCapability; + final @CapabilityState int suggestManualTimeCapability; if (!allowConfigDateTime) { - suggestManualTimeZoneCapability = CAPABILITY_NOT_ALLOWED; + suggestManualTimeCapability = CAPABILITY_NOT_ALLOWED; } else if (getAutoDetectionEnabledBehavior()) { - suggestManualTimeZoneCapability = CAPABILITY_NOT_APPLICABLE; + suggestManualTimeCapability = CAPABILITY_NOT_APPLICABLE; } else { - suggestManualTimeZoneCapability = CAPABILITY_POSSESSED; + suggestManualTimeCapability = CAPABILITY_POSSESSED; } - builder.setSetManualTimeCapability(suggestManualTimeZoneCapability); + builder.setSetManualTimeCapability(suggestManualTimeCapability); return builder.build(); } diff --git a/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java index 25a74ceeb56d..a39f64c1f82a 100644 --- a/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java +++ b/services/core/java/com/android/server/timedetector/ServiceConfigAccessor.java @@ -53,11 +53,15 @@ public interface ServiceConfigAccessor { /** * Updates the configuration properties that control a device's time behavior. * - * <p>This method returns {@code true} if the configuration was changed, - * {@code false} otherwise. + * <p>This method returns {@code true} if the configuration was changed, {@code false} + * otherwise. + * + * @param bypassUserPolicyChecks {@code true} for device policy manager use cases where device + * policy restrictions that should apply to actual users can be ignored */ boolean updateConfiguration( - @UserIdInt int userId, @NonNull TimeConfiguration requestedConfiguration); + @UserIdInt int userId, @NonNull TimeConfiguration requestedConfiguration, + boolean bypassUserPolicyChecks); /** * Returns a snapshot of the configuration that controls time zone detector behavior for the diff --git a/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java index 84013a755035..71acf35bd3a2 100644 --- a/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java +++ b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java @@ -194,11 +194,11 @@ final class ServiceConfigAccessorImpl implements ServiceConfigAccessor { @Override public synchronized boolean updateConfiguration(@UserIdInt int userId, - @NonNull TimeConfiguration requestedConfiguration) { + @NonNull TimeConfiguration requestedConfiguration, boolean bypassUserPolicyChecks) { Objects.requireNonNull(requestedConfiguration); - TimeCapabilitiesAndConfig capabilitiesAndConfig = - getCurrentUserConfigurationInternal().capabilitiesAndConfig(); + TimeCapabilitiesAndConfig capabilitiesAndConfig = getCurrentUserConfigurationInternal() + .createCapabilitiesAndConfig(bypassUserPolicyChecks); TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); TimeConfiguration oldConfiguration = capabilitiesAndConfig.getConfiguration(); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java b/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java index eae12c28d2b9..24533d79fb18 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorInternal.java @@ -17,16 +17,47 @@ package com.android.server.timedetector; import android.annotation.NonNull; +import android.app.time.TimeCapabilitiesAndConfig; +import android.app.time.TimeConfiguration; +import android.app.timedetector.ManualTimeSuggestion; /** - * The internal (in-process) system server API for the {@link - * com.android.server.timedetector.TimeDetectorService}. + * The internal (in-process) system server API for the time detector service. * * <p>The methods on this class can be called from any thread. + * + * <p>Methods marked with "[For device policy manager only]" are for use by the device policy + * manager to set device state and must not enforce device policy restrictions. + * * @hide */ public interface TimeDetectorInternal { + /** + * [For device policy manager only] Returns a snapshot of the configuration that controls time + * detector behavior for the current user. + */ + @NonNull + TimeCapabilitiesAndConfig getCapabilitiesAndConfigForDpm(); + + /** + * [For device policy manager only] Updates the configuration properties that control a device's + * time behavior for the current user. + * + * <p>This method returns {@code true} if the configuration was changed, {@code false} + * otherwise. + */ + boolean updateConfigurationForDpm(@NonNull TimeConfiguration configuration); + + /** + * [For device policy manager only] Attempts to set the device to a manually entered time. + * Returns {@code false} if the suggestion is invalid, or the device configuration prevents the + * suggestion being used, {@code true} if the suggestion has been accepted. A suggestion that is + * valid but does not change the time because it matches the current device time is considered + * accepted. + */ + boolean setManualTimeForDpm(@NonNull ManualTimeSuggestion manualTimeSuggestion); + /** Used to pass new network time suggestions to the time detector. */ void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java index 5a3e20ebd6cd..9839de080690 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorInternalImpl.java @@ -17,9 +17,14 @@ package com.android.server.timedetector; import android.annotation.NonNull; +import android.app.time.TimeCapabilitiesAndConfig; +import android.app.time.TimeConfiguration; +import android.app.timedetector.ManualTimeSuggestion; import android.content.Context; import android.os.Handler; +import com.android.server.timezonedetector.CurrentUserIdentityInjector; + import java.util.Objects; /** @@ -31,16 +36,50 @@ public class TimeDetectorInternalImpl implements TimeDetectorInternal { @NonNull private final Context mContext; @NonNull private final Handler mHandler; + @NonNull private final CurrentUserIdentityInjector mCurrentUserIdentityInjector; + @NonNull private final ServiceConfigAccessor mServiceConfigAccessor; @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy; public TimeDetectorInternalImpl(@NonNull Context context, @NonNull Handler handler, + @NonNull CurrentUserIdentityInjector currentUserIdentityInjector, + @NonNull ServiceConfigAccessor serviceConfigAccessor, @NonNull TimeDetectorStrategy timeDetectorStrategy) { mContext = Objects.requireNonNull(context); mHandler = Objects.requireNonNull(handler); + mCurrentUserIdentityInjector = Objects.requireNonNull(currentUserIdentityInjector); + mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor); mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy); } @Override + @NonNull + public TimeCapabilitiesAndConfig getCapabilitiesAndConfigForDpm() { + int currentUserId = mCurrentUserIdentityInjector.getCurrentUserId(); + final boolean bypassUserPolicyCheck = true; + ConfigurationInternal configurationInternal = + mServiceConfigAccessor.getConfigurationInternal(currentUserId); + return configurationInternal.createCapabilitiesAndConfig(bypassUserPolicyCheck); + } + + @Override + public boolean updateConfigurationForDpm(@NonNull TimeConfiguration configuration) { + Objects.requireNonNull(configuration); + + int currentUserId = mCurrentUserIdentityInjector.getCurrentUserId(); + final boolean bypassUserPolicyCheck = true; + return mServiceConfigAccessor.updateConfiguration( + currentUserId, configuration, bypassUserPolicyCheck); + } + + @Override + public boolean setManualTimeForDpm(@NonNull ManualTimeSuggestion timeSignal) { + Objects.requireNonNull(timeSignal); + + int userId = mCurrentUserIdentityInjector.getCurrentUserId(); + return mTimeDetectorStrategy.suggestManualTime(userId, timeSignal, false); + } + + @Override public void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal) { Objects.requireNonNull(timeSignal); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index 39672b847f30..64adbb680653 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java @@ -49,6 +49,7 @@ import com.android.internal.util.DumpUtils; import com.android.server.FgThread; import com.android.server.SystemService; import com.android.server.timezonedetector.CallerIdentityInjector; +import com.android.server.timezonedetector.CurrentUserIdentityInjector; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -84,8 +85,11 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub TimeDetectorStrategyImpl.create(context, handler, serviceConfigAccessor); // Create and publish the local service for use by internal callers. - TimeDetectorInternal internal = - new TimeDetectorInternalImpl(context, handler, timeDetectorStrategy); + CurrentUserIdentityInjector currentUserIdentityInjector = + CurrentUserIdentityInjector.REAL; + TimeDetectorInternal internal = new TimeDetectorInternalImpl( + context, handler, currentUserIdentityInjector, serviceConfigAccessor, + timeDetectorStrategy); publishLocalService(TimeDetectorInternal.class, internal); CallerIdentityInjector callerIdentityInjector = CallerIdentityInjector.REAL; @@ -147,7 +151,8 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub try { ConfigurationInternal configurationInternal = mServiceConfigAccessor.getConfigurationInternal(userId); - return configurationInternal.capabilitiesAndConfig(); + final boolean bypassUserPolicyCheck = false; + return configurationInternal.createCapabilitiesAndConfig(bypassUserPolicyCheck); } finally { mCallerIdentityInjector.restoreCallingIdentity(token); } @@ -170,7 +175,9 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub final long token = mCallerIdentityInjector.clearCallingIdentity(); try { - return mServiceConfigAccessor.updateConfiguration(resolvedUserId, configuration); + final boolean bypassUserPolicyCheck = false; + return mServiceConfigAccessor.updateConfiguration( + resolvedUserId, configuration, bypassUserPolicyCheck); } finally { mCallerIdentityInjector.restoreCallingIdentity(token); } @@ -313,7 +320,9 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub int userId = mCallerIdentityInjector.getCallingUserId(); final long token = Binder.clearCallingIdentity(); try { - return mTimeDetectorStrategy.suggestManualTime(userId, timeSignal); + final boolean bypassUserPolicyChecks = false; + return mTimeDetectorStrategy.suggestManualTime( + userId, timeSignal, bypassUserPolicyChecks); } finally { Binder.restoreCallingIdentity(token); } @@ -335,7 +344,9 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub int userId = mCallerIdentityInjector.getCallingUserId(); final long token = mCallerIdentityInjector.clearCallingIdentity(); try { - return mTimeDetectorStrategy.suggestManualTime(userId, timeSignal); + final boolean bypassUserPolicyChecks = false; + return mTimeDetectorStrategy.suggestManualTime( + userId, timeSignal, bypassUserPolicyChecks); } finally { mCallerIdentityInjector.restoreCallingIdentity(token); } @@ -400,19 +411,19 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub } private void enforceSuggestManualTimePermission() { - mContext.enforceCallingOrSelfPermission( + mContext.enforceCallingPermission( android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE, "suggest manual time and time zone"); } private void enforceSuggestNetworkTimePermission() { - mContext.enforceCallingOrSelfPermission( + mContext.enforceCallingPermission( android.Manifest.permission.SET_TIME, "set time"); } private void enforceSuggestGnssTimePermission() { - mContext.enforceCallingOrSelfPermission( + mContext.enforceCallingPermission( android.Manifest.permission.SET_TIME, "suggest gnss time"); } diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index bc86ed057fb6..03f236d9b30d 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -93,8 +93,12 @@ public interface TimeDetectorStrategy extends Dumpable { * invalid, or the device configuration prevented the suggestion being used, {@code true} if the * suggestion was accepted. A suggestion that is valid but does not change the time because it * matches the current device time is considered accepted. + * + * @param bypassUserPolicyChecks {@code true} for device policy manager use cases where device + * policy restrictions that should apply to actual users can be ignored */ - boolean suggestManualTime(@UserIdInt int userId, @NonNull ManualTimeSuggestion timeSuggestion); + boolean suggestManualTime(@UserIdInt int userId, @NonNull ManualTimeSuggestion timeSuggestion, + boolean bypassUserPolicyChecks); /** Processes the suggested time from network sources. */ void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index c3f05cc22495..3cee19cbe385 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -16,6 +16,8 @@ package com.android.server.timedetector; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; + import static com.android.server.SystemClockTime.TIME_CONFIDENCE_HIGH; import static com.android.server.SystemClockTime.TIME_CONFIDENCE_LOW; import static com.android.server.timedetector.TimeDetectorStrategy.originToString; @@ -26,6 +28,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.time.ExternalTimeSuggestion; +import android.app.time.TimeCapabilities; +import android.app.time.TimeCapabilitiesAndConfig; import android.app.time.TimeState; import android.app.time.UnixEpochTime; import android.app.timedetector.ManualTimeSuggestion; @@ -244,7 +248,8 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { @Override public synchronized boolean suggestManualTime( - @UserIdInt int userId, @NonNull ManualTimeSuggestion suggestion) { + @UserIdInt int userId, @NonNull ManualTimeSuggestion suggestion, + boolean bypassUserPolicyChecks) { ConfigurationInternal currentUserConfig = mCurrentConfigurationInternal; if (currentUserConfig.getUserId() != userId) { @@ -256,6 +261,18 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } Objects.requireNonNull(suggestion); + String cause = "Manual time suggestion received: suggestion=" + suggestion; + + TimeCapabilitiesAndConfig capabilitiesAndConfig = + currentUserConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); + TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); + if (capabilities.getSetManualTimeCapability() != CAPABILITY_POSSESSED) { + Slog.i(LOG_TAG, "User does not have the capability needed to set the time manually" + + ": capabilities=" + capabilities + + ", suggestion=" + suggestion + + ", cause=" + cause); + return false; + } final UnixEpochTime newUnixEpochTime = suggestion.getUnixEpochTime(); @@ -263,7 +280,6 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { return false; } - String cause = "Manual time suggestion received: suggestion=" + suggestion; return setSystemClockAndConfidenceIfRequired(ORIGIN_MANUAL, newUnixEpochTime, cause); } @@ -428,7 +444,10 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet); ipw.println("mCurrentConfigurationInternal=" + mCurrentConfigurationInternal); - ipw.println("[Capabilities=" + mCurrentConfigurationInternal.capabilitiesAndConfig() + "]"); + final boolean bypassUserPolicyChecks = false; + ipw.println("[Capabilities=" + + mCurrentConfigurationInternal.createCapabilitiesAndConfig(bypassUserPolicyChecks) + + "]"); long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis(); ipw.printf("mEnvironment.elapsedRealtimeMillis()=%s (%s)\n", Duration.ofMillis(elapsedRealtimeMillis), elapsedRealtimeMillis); diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java index d413febdcdb4..8e2a5f472558 100644 --- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java +++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java @@ -147,7 +147,13 @@ public final class ConfigurationInternal { return UserHandle.of(mUserId); } - /** Returns true if the user allowed to modify time zone configuration. */ + /** + * Returns true if the user is allowed to modify time zone configuration, e.g. can be false due + * to device policy (enterprise). + * + * <p>See also {@link #createCapabilitiesAndConfig(boolean)} for situations where this value + * are ignored. + */ public boolean isUserConfigAllowed() { return mUserConfigAllowed; } @@ -190,17 +196,24 @@ public final class ConfigurationInternal { || getGeoDetectionRunInBackgroundEnabled()); } - /** Creates a {@link TimeZoneCapabilitiesAndConfig} object using the configuration values. */ - public TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig() { - return new TimeZoneCapabilitiesAndConfig(asCapabilities(), asConfiguration()); + /** + * Creates a {@link TimeZoneCapabilitiesAndConfig} object using the configuration values. + * + * @param bypassUserPolicyChecks {@code true} for device policy manager use cases where device + * policy restrictions that should apply to actual users can be ignored + */ + public TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig( + boolean bypassUserPolicyChecks) { + return new TimeZoneCapabilitiesAndConfig( + asCapabilities(bypassUserPolicyChecks), asConfiguration()); } @NonNull - private TimeZoneCapabilities asCapabilities() { + private TimeZoneCapabilities asCapabilities(boolean bypassUserPolicyChecks) { UserHandle userHandle = UserHandle.of(mUserId); TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(userHandle); - boolean allowConfigDateTime = isUserConfigAllowed(); + boolean allowConfigDateTime = isUserConfigAllowed() || bypassUserPolicyChecks; // Automatic time zone detection is only supported on devices if there is a telephony // network available or geolocation time zone detection is possible. diff --git a/services/core/java/com/android/server/timezonedetector/CurrentUserIdentityInjector.java b/services/core/java/com/android/server/timezonedetector/CurrentUserIdentityInjector.java new file mode 100644 index 000000000000..f96b76dca929 --- /dev/null +++ b/services/core/java/com/android/server/timezonedetector/CurrentUserIdentityInjector.java @@ -0,0 +1,46 @@ +/* + * Copyright 2022 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 com.android.server.timezonedetector; + +import android.annotation.UserIdInt; +import android.app.ActivityManagerInternal; + +import com.android.server.LocalServices; + +/** + * An interface to access the current user identity in an easy to fake for tests way. + */ +public interface CurrentUserIdentityInjector { + + /** A singleton for the real implementation of {@link CurrentUserIdentityInjector}. */ + CurrentUserIdentityInjector REAL = new Real(); + + /** A {@link ActivityManagerInternal#getCurrentUserId()} call. */ + @UserIdInt int getCurrentUserId(); + + /** The real implementation of {@link CurrentUserIdentityInjector}. */ + class Real implements CurrentUserIdentityInjector { + + protected Real() { + } + + @Override + public int getCurrentUserId() { + return LocalServices.getService(ActivityManagerInternal.class).getCurrentUserId(); + } + } +} diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java index 692b0cc795f5..8da5d6aefdd6 100644 --- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java +++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessor.java @@ -81,11 +81,15 @@ public interface ServiceConfigAccessor { /** * Updates the configuration properties that control a device's time zone behavior. * - * <p>This method returns {@code true} if the configuration was changed, - * {@code false} otherwise. + * <p>This method returns {@code true} if the configuration was changed, {@code false} + * otherwise. + * + * @param bypassUserPolicyChecks {@code true} for device policy manager use cases where device + * policy restrictions that should apply to actual users can be ignored */ - boolean updateConfiguration(@UserIdInt int userId, - @NonNull TimeZoneConfiguration requestedConfiguration); + boolean updateConfiguration( + @UserIdInt int userId, @NonNull TimeZoneConfiguration requestedConfiguration, + boolean bypassUserPolicyChecks); /** * Returns a snapshot of the configuration that controls time zone detector behavior for the diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java index 7173f6008177..e2f42467c550 100644 --- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java +++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java @@ -234,11 +234,11 @@ public final class ServiceConfigAccessorImpl implements ServiceConfigAccessor { @Override public synchronized boolean updateConfiguration(@UserIdInt int userId, - @NonNull TimeZoneConfiguration requestedConfiguration) { + @NonNull TimeZoneConfiguration requestedConfiguration, boolean bypassUserPolicyChecks) { Objects.requireNonNull(requestedConfiguration); - TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = - getConfigurationInternal(userId).createCapabilitiesAndConfig(); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = getConfigurationInternal(userId) + .createCapabilitiesAndConfig(bypassUserPolicyChecks); TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); TimeZoneConfiguration oldConfiguration = capabilitiesAndConfig.getConfiguration(); diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java index b6ce8026a2dc..80cf1d6b9031 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java @@ -17,17 +17,48 @@ package com.android.server.timezonedetector; import android.annotation.NonNull; +import android.app.time.TimeZoneCapabilitiesAndConfig; +import android.app.time.TimeZoneConfiguration; +import android.app.timezonedetector.ManualTimeZoneSuggestion; /** - * The internal (in-process) system server API for the {@link - * com.android.server.timezonedetector.TimeZoneDetectorService}. + * The internal (in-process) system server API for the time zone detector service. * * <p>The methods on this class can be called from any thread. + * + * <p>Methods marked with "[For device policy manager only]" are for use by the device policy + * manager to set device state and must not enforce device policy restrictions. + * * @hide */ public interface TimeZoneDetectorInternal { /** + * [For device policy manager only] Returns a snapshot of the configuration that controls time + * zone detector behavior for the current user. + */ + @NonNull + TimeZoneCapabilitiesAndConfig getCapabilitiesAndConfigForDpm(); + + /** + * [For device policy manager only] Updates the configuration properties that control a device's + * time zone behavior for the current user. + * + * <p>This method returns {@code true} if the configuration was changed, + * {@code false} otherwise. + */ + boolean updateConfigurationForDpm(@NonNull TimeZoneConfiguration configuration); + + /** + * [For device policy manager only] Attempts to set the device to a manually entered time zone. + * Returns {@code false} if the suggestion is invalid, or the device configuration prevents the + * suggestion being used, {@code true} if the suggestion has been accepted. A suggestion that is + * valid but does not change the time zone because it matches the current device time zone is + * considered accepted. + */ + boolean setManualTimeZoneForDpm(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion); + + /** * Suggests the current time zone, determined using geolocation, to the detector. The * detector may ignore the signal based on system settings, whether better information is * available, and so on. This method may be implemented asynchronously. diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java index f61df820c3e0..ce64eaccd98f 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java @@ -17,6 +17,9 @@ package com.android.server.timezonedetector; import android.annotation.NonNull; +import android.app.time.TimeZoneCapabilitiesAndConfig; +import android.app.time.TimeZoneConfiguration; +import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.content.Context; import android.os.Handler; @@ -31,16 +34,52 @@ public final class TimeZoneDetectorInternalImpl implements TimeZoneDetectorInter @NonNull private final Context mContext; @NonNull private final Handler mHandler; + @NonNull private final CurrentUserIdentityInjector mCurrentUserIdentityInjector; + @NonNull private final ServiceConfigAccessor mServiceConfigAccessor; @NonNull private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy; public TimeZoneDetectorInternalImpl(@NonNull Context context, @NonNull Handler handler, + @NonNull CurrentUserIdentityInjector currentUserIdentityInjector, + @NonNull ServiceConfigAccessor serviceConfigAccessor, @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) { mContext = Objects.requireNonNull(context); mHandler = Objects.requireNonNull(handler); + mCurrentUserIdentityInjector = Objects.requireNonNull(currentUserIdentityInjector); + mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor); mTimeZoneDetectorStrategy = Objects.requireNonNull(timeZoneDetectorStrategy); } @Override + @NonNull + public TimeZoneCapabilitiesAndConfig getCapabilitiesAndConfigForDpm() { + int currentUserId = mCurrentUserIdentityInjector.getCurrentUserId(); + ConfigurationInternal configurationInternal = + mServiceConfigAccessor.getConfigurationInternal(currentUserId); + final boolean bypassUserPolicyChecks = true; + return configurationInternal.createCapabilitiesAndConfig(bypassUserPolicyChecks); + } + + @Override + public boolean updateConfigurationForDpm(@NonNull TimeZoneConfiguration configuration) { + Objects.requireNonNull(configuration); + + int currentUserId = mCurrentUserIdentityInjector.getCurrentUserId(); + final boolean bypassUserPolicyChecks = true; + return mServiceConfigAccessor.updateConfiguration( + currentUserId, configuration, bypassUserPolicyChecks); + } + + @Override + public boolean setManualTimeZoneForDpm(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) { + Objects.requireNonNull(timeZoneSuggestion); + + int currentUserId = mCurrentUserIdentityInjector.getCurrentUserId(); + final boolean bypassUserPolicyChecks = true; + return mTimeZoneDetectorStrategy.suggestManualTimeZone( + currentUserId, timeZoneSuggestion, bypassUserPolicyChecks); + } + + @Override public void suggestGeolocationTimeZone( @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) { Objects.requireNonNull(timeZoneSuggestion); diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index 822cd4124380..13f169461511 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java @@ -96,8 +96,11 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub }); // Create and publish the local service for use by internal callers. - TimeZoneDetectorInternal internal = - new TimeZoneDetectorInternalImpl(context, handler, timeZoneDetectorStrategy); + CurrentUserIdentityInjector currentUserIdentityInjector = + CurrentUserIdentityInjector.REAL; + TimeZoneDetectorInternal internal = new TimeZoneDetectorInternalImpl( + context, handler, currentUserIdentityInjector, serviceConfigAccessor, + timeZoneDetectorStrategy); publishLocalService(TimeZoneDetectorInternal.class, internal); // Publish the binder service so it can be accessed from other (appropriately @@ -175,7 +178,8 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub try { ConfigurationInternal configurationInternal = mServiceConfigAccessor.getConfigurationInternal(userId); - return configurationInternal.createCapabilitiesAndConfig(); + final boolean bypassUserPolicyChecks = false; + return configurationInternal.createCapabilitiesAndConfig(bypassUserPolicyChecks); } finally { mCallerIdentityInjector.restoreCallingIdentity(token); } @@ -199,7 +203,9 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub final long token = mCallerIdentityInjector.clearCallingIdentity(); try { - return mServiceConfigAccessor.updateConfiguration(resolvedUserId, configuration); + final boolean bypassUserPolicyChecks = false; + return mServiceConfigAccessor.updateConfiguration( + resolvedUserId, configuration, bypassUserPolicyChecks); } finally { mCallerIdentityInjector.restoreCallingIdentity(token); } @@ -350,7 +356,9 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub int userId = mCallerIdentityInjector.getCallingUserId(); final long token = mCallerIdentityInjector.clearCallingIdentity(); try { - return mTimeZoneDetectorStrategy.suggestManualTimeZone(userId, timeZoneSuggestion); + final boolean bypassUserPolicyChecks = false; + return mTimeZoneDetectorStrategy.suggestManualTimeZone( + userId, timeZoneSuggestion, bypassUserPolicyChecks); } finally { mCallerIdentityInjector.restoreCallingIdentity(token); } @@ -364,7 +372,9 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub int userId = mCallerIdentityInjector.getCallingUserId(); final long token = mCallerIdentityInjector.clearCallingIdentity(); try { - return mTimeZoneDetectorStrategy.suggestManualTimeZone(userId, timeZoneSuggestion); + final boolean bypassUserPolicyChecks = false; + return mTimeZoneDetectorStrategy.suggestManualTimeZone( + userId, timeZoneSuggestion, bypassUserPolicyChecks); } finally { mCallerIdentityInjector.restoreCallingIdentity(token); } @@ -449,7 +459,7 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub private void enforceSuggestGeolocationTimeZonePermission() { // The associated method is only used for the shell command interface, it's not possible to // call it via Binder, and Shell currently can set the time zone directly anyway. - mContext.enforceCallingOrSelfPermission( + mContext.enforceCallingPermission( android.Manifest.permission.SET_TIME_ZONE, "suggest geolocation time zone"); } @@ -461,7 +471,7 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub } private void enforceSuggestManualTimeZonePermission() { - mContext.enforceCallingOrSelfPermission( + mContext.enforceCallingPermission( android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE, "suggest manual time and time zone"); } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java index e4b2df1460ae..69284e322663 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java @@ -120,9 +120,13 @@ public interface TimeZoneDetectorStrategy extends Dumpable { /** * Suggests a time zone for the device using manually-entered (i.e. user sourced) information. + * + * @param bypassUserPolicyChecks {@code true} for device policy manager use cases where device + * policy restrictions that should apply to actual users can be ignored */ boolean suggestManualTimeZone( - @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion); + @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion, + boolean bypassUserPolicyChecks); /** * Suggests a time zone for the device, or withdraws a previous suggestion if diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java index 1e88c47e7b30..18c8885ee059 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java @@ -317,7 +317,8 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat @Override public synchronized boolean suggestManualTimeZone( - @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion) { + @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion, + boolean bypassUserPolicyChecks) { ConfigurationInternal currentUserConfig = mCurrentConfigurationInternal; if (currentUserConfig.getUserId() != userId) { @@ -334,7 +335,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat String cause = "Manual time suggestion received: suggestion=" + suggestion; TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = - currentUserConfig.createCapabilitiesAndConfig(); + currentUserConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); if (capabilities.getSetManualTimeZoneCapability() != CAPABILITY_POSSESSED) { Slog.i(LOG_TAG, "User does not have the capability needed to set the time zone manually" @@ -757,7 +758,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat ipw.increaseIndent(); // level 1 ipw.println("mCurrentConfigurationInternal=" + mCurrentConfigurationInternal); - ipw.println("[Capabilities=" + mCurrentConfigurationInternal.createCapabilitiesAndConfig() + final boolean bypassUserPolicyChecks = false; + ipw.println("[Capabilities=" + + mCurrentConfigurationInternal.createCapabilitiesAndConfig(bypassUserPolicyChecks) + "]"); ipw.println("mEnvironment.getDeviceTimeZone()=" + mEnvironment.getDeviceTimeZone()); ipw.println("mEnvironment.getDeviceTimeZoneConfidence()=" diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java index a24afe6bc25e..808c1ec2603b 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java @@ -31,8 +31,6 @@ import android.app.time.TimeCapabilities; import android.app.time.TimeCapabilitiesAndConfig; import android.app.time.TimeConfiguration; -import androidx.test.runner.AndroidJUnit4; - import com.android.server.timedetector.TimeDetectorStrategy.Origin; import org.junit.Test; @@ -40,7 +38,14 @@ import org.junit.runner.RunWith; import java.time.Instant; -@RunWith(AndroidJUnit4.class) +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + +/** + * Tests for {@link ConfigurationInternal} and associated {@link TimeCapabilitiesAndConfig} + * behavior. + */ +@RunWith(JUnitParamsRunner.class) public class ConfigurationInternalTest { private static final int ARBITRARY_USER_ID = 99999; @@ -51,14 +56,15 @@ public class ConfigurationInternalTest { private static final @Origin int[] ARBITRARY_ORIGIN_PRIORITIES = { ORIGIN_NETWORK }; /** - * Tests when {@link ConfigurationInternal#isUserConfigAllowed()} and - * {@link ConfigurationInternal#isAutoDetectionSupported()} are both true. + * Tests {@link TimeCapabilitiesAndConfig} behavior in different scenarios when auto detection + * is supported. */ @Test - public void test_unrestricted() { - ConfigurationInternal - baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) - .setUserConfigAllowed(true) + @Parameters({ "true,true", "true,false", "false,true", "false,false" }) + public void test_autoDetectionSupported_capabilitiesAndConfiguration( + boolean userConfigAllowed, boolean bypassUserPolicyChecks) { + ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) + .setUserConfigAllowed(userConfigAllowed) .setAutoDetectionSupported(true) .setSystemClockUpdateThresholdMillis(ARBITRARY_SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS) .setAutoSuggestionLowerBound(ARBITRARY_SUGGESTION_LOWER_BOUND) @@ -67,58 +73,8 @@ public class ConfigurationInternalTest { .setOriginPriorities(ARBITRARY_ORIGIN_PRIORITIES) .setAutoDetectionEnabledSetting(true) .build(); - { - ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig) - .setAutoDetectionEnabledSetting(true) - .build(); - assertTrue(autoOnConfig.getAutoDetectionEnabledSetting()); - assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior()); - - TimeCapabilitiesAndConfig capabilitiesAndConfig = autoOnConfig.capabilitiesAndConfig(); - - TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); - assertEquals(CAPABILITY_POSSESSED, - capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSetManualTimeCapability()); - - TimeConfiguration configuration = capabilitiesAndConfig.getConfiguration(); - assertTrue(configuration.isAutoDetectionEnabled()); - } - { - ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig) - .setAutoDetectionEnabledSetting(false) - .build(); - assertFalse(autoOffConfig.getAutoDetectionEnabledSetting()); - assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior()); - - TimeCapabilitiesAndConfig capabilitiesAndConfig = autoOffConfig.capabilitiesAndConfig(); - - TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); - assertEquals(CAPABILITY_POSSESSED, - capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_POSSESSED, - capabilities.getSetManualTimeCapability()); - - TimeConfiguration configuration = capabilitiesAndConfig.getConfiguration(); - assertFalse(configuration.isAutoDetectionEnabled()); - } - } - - /** Tests when {@link ConfigurationInternal#isUserConfigAllowed()} is false */ - @Test - public void test_restricted() { - ConfigurationInternal - baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) - .setUserConfigAllowed(false) - .setAutoDetectionSupported(true) - .setSystemClockUpdateThresholdMillis(ARBITRARY_SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS) - .setAutoSuggestionLowerBound(ARBITRARY_SUGGESTION_LOWER_BOUND) - .setManualSuggestionLowerBound(ARBITRARY_SUGGESTION_LOWER_BOUND) - .setSuggestionUpperBound(ARBITRARY_SUGGESTION_UPPER_BOUND) - .setOriginPriorities(ARBITRARY_ORIGIN_PRIORITIES) - .setAutoDetectionEnabledSetting(true) - .build(); + boolean userRestrictionsExpected = !(userConfigAllowed || bypassUserPolicyChecks); { ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig) .setAutoDetectionEnabledSetting(true) @@ -126,12 +82,19 @@ public class ConfigurationInternalTest { assertTrue(autoOnConfig.getAutoDetectionEnabledSetting()); assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior()); - TimeCapabilitiesAndConfig capabilitiesAndConfig = autoOnConfig.capabilitiesAndConfig(); + TimeCapabilitiesAndConfig capabilitiesAndConfig = + autoOnConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); - assertEquals(CAPABILITY_NOT_ALLOWED, - capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeCapability()); + if (userRestrictionsExpected) { + assertEquals(CAPABILITY_NOT_ALLOWED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeCapability()); + } else { + assertEquals(CAPABILITY_POSSESSED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSetManualTimeCapability()); + } TimeConfiguration configuration = capabilitiesAndConfig.getConfiguration(); assertTrue(configuration.isAutoDetectionEnabled()); @@ -144,23 +107,35 @@ public class ConfigurationInternalTest { assertFalse(autoOffConfig.getAutoDetectionEnabledSetting()); assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior()); - TimeCapabilitiesAndConfig capabilitiesAndConfig = autoOffConfig.capabilitiesAndConfig(); + TimeCapabilitiesAndConfig capabilitiesAndConfig = + autoOffConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); - assertEquals(CAPABILITY_NOT_ALLOWED, - capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeCapability()); - + if (userRestrictionsExpected) { + assertEquals(CAPABILITY_NOT_ALLOWED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeCapability()); + } else { + assertEquals(CAPABILITY_POSSESSED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_POSSESSED, + capabilities.getSetManualTimeCapability()); + } TimeConfiguration configuration = capabilitiesAndConfig.getConfiguration(); assertFalse(configuration.isAutoDetectionEnabled()); } } - /** Tests when {@link ConfigurationInternal#isAutoDetectionSupported()} is false. */ + /** + * Tests {@link TimeCapabilitiesAndConfig} behavior in different scenarios when auto detection + * is not supported. + */ @Test - public void test_autoDetectNotSupported() { + @Parameters({ "true,true", "true,false", "false,true", "false,false" }) + public void test_autoDetectNotSupported_capabilitiesAndConfiguration( + boolean userConfigAllowed, boolean bypassUserPolicyChecks) { ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) - .setUserConfigAllowed(true) + .setUserConfigAllowed(userConfigAllowed) .setAutoDetectionSupported(false) .setSystemClockUpdateThresholdMillis(ARBITRARY_SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS) .setAutoSuggestionLowerBound(ARBITRARY_SUGGESTION_LOWER_BOUND) @@ -169,6 +144,9 @@ public class ConfigurationInternalTest { .setOriginPriorities(ARBITRARY_ORIGIN_PRIORITIES) .setAutoDetectionEnabledSetting(true) .build(); + boolean userRestrictionsExpected = !(userConfigAllowed || bypassUserPolicyChecks); + + // Auto-detection enabled. { ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig) .setAutoDetectionEnabledSetting(true) @@ -176,30 +154,41 @@ public class ConfigurationInternalTest { assertTrue(autoOnConfig.getAutoDetectionEnabledSetting()); assertFalse(autoOnConfig.getAutoDetectionEnabledBehavior()); - TimeCapabilitiesAndConfig capabilitiesAndConfig = autoOnConfig.capabilitiesAndConfig(); + TimeCapabilitiesAndConfig capabilitiesAndConfig = + autoOnConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeCapability()); + if (userRestrictionsExpected) { + assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeCapability()); + } else { + assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeCapability()); + } TimeConfiguration configuration = capabilitiesAndConfig.getConfiguration(); assertTrue(configuration.isAutoDetectionEnabled()); } + + // Auto-detection disabled. { - ConfigurationInternal - autoOffConfig = new ConfigurationInternal.Builder(baseConfig) + ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig) .setAutoDetectionEnabledSetting(false) .build(); assertFalse(autoOffConfig.getAutoDetectionEnabledSetting()); assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior()); - TimeCapabilitiesAndConfig capabilitiesAndConfig = autoOffConfig.capabilitiesAndConfig(); + TimeCapabilitiesAndConfig capabilitiesAndConfig = + autoOffConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeCapability()); + if (userRestrictionsExpected) { + assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeCapability()); + } else { + assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeCapability()); + } TimeConfiguration configuration = capabilitiesAndConfig.getConfiguration(); assertFalse(configuration.isAutoDetectionEnabled()); diff --git a/services/tests/servicestests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java b/services/tests/servicestests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java index 77b319b56acb..a98a43b75d9b 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/FakeServiceConfigAccessor.java @@ -31,7 +31,7 @@ import java.util.ArrayList; import java.util.List; /** A partially implemented, fake implementation of ServiceConfigAccessor for tests. */ -class FakeServiceConfigAccessor implements ServiceConfigAccessor { +public class FakeServiceConfigAccessor implements ServiceConfigAccessor { private final List<ConfigurationChangeListener> mConfigurationInternalChangeListeners = new ArrayList<>(); @@ -54,7 +54,8 @@ class FakeServiceConfigAccessor implements ServiceConfigAccessor { @Override public boolean updateConfiguration( - @UserIdInt int userID, @NonNull TimeConfiguration requestedChanges) { + @UserIdInt int userID, @NonNull TimeConfiguration requestedChanges, + boolean bypassUserPolicyChecks) { assertNotNull(mConfigurationInternal); assertNotNull(requestedChanges); @@ -62,7 +63,7 @@ class FakeServiceConfigAccessor implements ServiceConfigAccessor { // old configuration merged with the new if the user has the capability to up the settings. // Then, if the configuration changed, the change listener is invoked. TimeCapabilitiesAndConfig capabilitiesAndConfig = - mConfigurationInternal.capabilitiesAndConfig(); + mConfigurationInternal.createCapabilitiesAndConfig(bypassUserPolicyChecks); TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); TimeConfiguration configuration = capabilitiesAndConfig.getConfiguration(); TimeConfiguration newConfiguration = diff --git a/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java index 7b38fa014512..856df359b326 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/FakeTimeDetectorStrategy.java @@ -16,9 +16,6 @@ package com.android.server.timedetector; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import android.annotation.UserIdInt; import android.app.time.ExternalTimeSuggestion; import android.app.time.TimeState; @@ -31,20 +28,10 @@ import android.util.IndentingPrintWriter; * A fake implementation of {@link com.android.server.timedetector.TimeDetectorStrategy} for use * in tests. */ -class FakeTimeDetectorStrategy implements TimeDetectorStrategy { +public class FakeTimeDetectorStrategy implements TimeDetectorStrategy { // State private TimeState mTimeState; - // Call tracking. - private TelephonyTimeSuggestion mLastTelephonySuggestion; - private @UserIdInt Integer mLastManualSuggestionUserId; - private ManualTimeSuggestion mLastManualSuggestion; - private NetworkTimeSuggestion mLastNetworkSuggestion; - private GnssTimeSuggestion mLastGnssSuggestion; - private ExternalTimeSuggestion mLastExternalSuggestion; - private UnixEpochTime mLastConfirmedTime; - private boolean mDumpCalled; - @Override public TimeState getTimeState() { return mTimeState; @@ -57,80 +44,32 @@ class FakeTimeDetectorStrategy implements TimeDetectorStrategy { @Override public boolean confirmTime(UnixEpochTime confirmationTime) { - mLastConfirmedTime = confirmationTime; return false; } @Override public void suggestTelephonyTime(TelephonyTimeSuggestion timeSuggestion) { - mLastTelephonySuggestion = timeSuggestion; } @Override - public boolean suggestManualTime(@UserIdInt int userId, ManualTimeSuggestion timeSuggestion) { - mLastManualSuggestionUserId = userId; - mLastManualSuggestion = timeSuggestion; + public boolean suggestManualTime(@UserIdInt int userId, ManualTimeSuggestion timeSuggestion, + boolean bypassUserPolicyChecks) { return true; } @Override public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) { - mLastNetworkSuggestion = timeSuggestion; } @Override public void suggestGnssTime(GnssTimeSuggestion timeSuggestion) { - mLastGnssSuggestion = timeSuggestion; } @Override public void suggestExternalTime(ExternalTimeSuggestion timeSuggestion) { - mLastExternalSuggestion = timeSuggestion; } @Override public void dump(IndentingPrintWriter pw, String[] args) { - mDumpCalled = true; - } - - void resetCallTracking() { - mLastTelephonySuggestion = null; - mLastManualSuggestionUserId = null; - mLastManualSuggestion = null; - mLastNetworkSuggestion = null; - mLastGnssSuggestion = null; - mLastExternalSuggestion = null; - mLastConfirmedTime = null; - mDumpCalled = false; - } - - void verifySuggestTelephonyTimeCalled(TelephonyTimeSuggestion expectedSuggestion) { - assertEquals(expectedSuggestion, mLastTelephonySuggestion); - } - - void verifySuggestManualTimeCalled( - @UserIdInt int expectedUserId, ManualTimeSuggestion expectedSuggestion) { - assertEquals((Integer) expectedUserId, mLastManualSuggestionUserId); - assertEquals(expectedSuggestion, mLastManualSuggestion); - } - - void verifySuggestNetworkTimeCalled(NetworkTimeSuggestion expectedSuggestion) { - assertEquals(expectedSuggestion, mLastNetworkSuggestion); - } - - void verifySuggestGnssTimeCalled(GnssTimeSuggestion expectedSuggestion) { - assertEquals(expectedSuggestion, mLastGnssSuggestion); - } - - void verifySuggestExternalTimeCalled(ExternalTimeSuggestion expectedSuggestion) { - assertEquals(expectedSuggestion, mLastExternalSuggestion); - } - - void verifyConfirmTimeCalled(UnixEpochTime expectedConfirmationTime) { - assertEquals(mLastConfirmedTime, expectedConfirmationTime); - } - - void verifyDumpCalled() { - assertTrue(mDumpCalled); } } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java index 5b6175217568..a0845a64757e 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorInternalImplTest.java @@ -16,14 +16,24 @@ package com.android.server.timedetector; +import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import android.app.time.TimeCapabilitiesAndConfig; +import android.app.time.TimeConfiguration; import android.app.time.UnixEpochTime; +import android.app.timedetector.ManualTimeSuggestion; import android.content.Context; import android.os.HandlerThread; import androidx.test.runner.AndroidJUnit4; +import com.android.server.timezonedetector.TestCurrentUserIdentityInjector; import com.android.server.timezonedetector.TestHandler; import org.junit.After; @@ -31,15 +41,25 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.time.Instant; + @RunWith(AndroidJUnit4.class) public class TimeDetectorInternalImplTest { + private static final int ARBITRARY_USER_ID = 9999; + private static final int ARBITRARY_SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS = 1234; + private static final Instant ARBITRARY_SUGGESTION_LOWER_BOUND = Instant.ofEpochMilli(0); + private static final Instant ARBITRARY_SUGGESTION_UPPER_BOUND = + Instant.ofEpochMilli(Long.MAX_VALUE); + private static final int[] ARBITRARY_ORIGIN_PRIORITIES = { ORIGIN_NETWORK }; private Context mMockContext; - private FakeTimeDetectorStrategy mFakeTimeDetectorStrategy; - - private TimeDetectorInternalImpl mTimeDetectorInternal; private HandlerThread mHandlerThread; private TestHandler mTestHandler; + private TestCurrentUserIdentityInjector mTestCurrentUserIdentityInjector; + private FakeServiceConfigAccessor mFakeServiceConfigAccessorSpy; + private FakeTimeDetectorStrategy mFakeTimeDetectorStrategySpy; + + private TimeDetectorInternalImpl mTimeDetectorInternal; @Before public void setUp() { @@ -49,11 +69,14 @@ public class TimeDetectorInternalImplTest { mHandlerThread = new HandlerThread("TimeDetectorInternalTest"); mHandlerThread.start(); mTestHandler = new TestHandler(mHandlerThread.getLooper()); - - mFakeTimeDetectorStrategy = new FakeTimeDetectorStrategy(); + mTestCurrentUserIdentityInjector = new TestCurrentUserIdentityInjector(); + mTestCurrentUserIdentityInjector.initializeCurrentUserId(ARBITRARY_USER_ID); + mFakeServiceConfigAccessorSpy = spy(new FakeServiceConfigAccessor()); + mFakeTimeDetectorStrategySpy = spy(new FakeTimeDetectorStrategy()); mTimeDetectorInternal = new TimeDetectorInternalImpl( - mMockContext, mTestHandler, mFakeTimeDetectorStrategy); + mMockContext, mTestHandler, mTestCurrentUserIdentityInjector, + mFakeServiceConfigAccessorSpy, mFakeTimeDetectorStrategySpy); } @After @@ -63,6 +86,56 @@ public class TimeDetectorInternalImplTest { } @Test + public void testGetCapabilitiesAndConfigForDpm() throws Exception { + final boolean autoDetectionEnabled = true; + ConfigurationInternal testConfig = createConfigurationInternal(autoDetectionEnabled); + mFakeServiceConfigAccessorSpy.initializeConfiguration(testConfig); + + TimeCapabilitiesAndConfig actualCapabilitiesAndConfig = + mTimeDetectorInternal.getCapabilitiesAndConfigForDpm(); + + int expectedUserId = mTestCurrentUserIdentityInjector.getCurrentUserId(); + verify(mFakeServiceConfigAccessorSpy).getConfigurationInternal(expectedUserId); + + final boolean bypassUserPolicyChecks = true; + TimeCapabilitiesAndConfig expectedCapabilitiesAndConfig = + testConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); + assertEquals(expectedCapabilitiesAndConfig, actualCapabilitiesAndConfig); + } + + @Test + public void testUpdateConfigurationForDpm() throws Exception { + final boolean autoDetectionEnabled = false; + ConfigurationInternal initialConfigurationInternal = + createConfigurationInternal(autoDetectionEnabled); + mFakeServiceConfigAccessorSpy.initializeConfiguration(initialConfigurationInternal); + + TimeConfiguration timeConfiguration = new TimeConfiguration.Builder() + .setAutoDetectionEnabled(true) + .build(); + assertTrue(mTimeDetectorInternal.updateConfigurationForDpm(timeConfiguration)); + + final boolean expectedBypassUserPolicyChecks = true; + verify(mFakeServiceConfigAccessorSpy).updateConfiguration( + mTestCurrentUserIdentityInjector.getCurrentUserId(), + timeConfiguration, + expectedBypassUserPolicyChecks); + } + + @Test + public void testSetManualTimeZoneForDpm() throws Exception { + ManualTimeSuggestion timeSuggestion = createManualTimeSuggestion(); + + // The fake strategy always returns true. + assertTrue(mTimeDetectorInternal.setManualTimeForDpm(timeSuggestion)); + + int expectedUserId = mTestCurrentUserIdentityInjector.getCurrentUserId(); + boolean expectedBypassUserPolicyChecks = false; + verify(mFakeTimeDetectorStrategySpy).suggestManualTime( + expectedUserId, timeSuggestion, expectedBypassUserPolicyChecks); + } + + @Test public void testSuggestNetworkTime() throws Exception { NetworkTimeSuggestion networkTimeSuggestion = createNetworkTimeSuggestion(); @@ -70,7 +143,7 @@ public class TimeDetectorInternalImplTest { mTestHandler.assertTotalMessagesEnqueued(1); mTestHandler.waitForMessagesToBeProcessed(); - mFakeTimeDetectorStrategy.verifySuggestNetworkTimeCalled(networkTimeSuggestion); + verify(mFakeTimeDetectorStrategySpy).suggestNetworkTime(networkTimeSuggestion); } private static NetworkTimeSuggestion createNetworkTimeSuggestion() { @@ -86,11 +159,29 @@ public class TimeDetectorInternalImplTest { mTestHandler.assertTotalMessagesEnqueued(1); mTestHandler.waitForMessagesToBeProcessed(); - mFakeTimeDetectorStrategy.verifySuggestGnssTimeCalled(gnssTimeSuggestion); + verify(mFakeTimeDetectorStrategySpy).suggestGnssTime(gnssTimeSuggestion); + } + + private static ManualTimeSuggestion createManualTimeSuggestion() { + UnixEpochTime timeValue = new UnixEpochTime(100L, 1_000_000L); + return new ManualTimeSuggestion(timeValue); } private static GnssTimeSuggestion createGnssTimeSuggestion() { UnixEpochTime timeValue = new UnixEpochTime(100L, 1_000_000L); return new GnssTimeSuggestion(timeValue); } + + private static ConfigurationInternal createConfigurationInternal(boolean autoDetectionEnabled) { + return new ConfigurationInternal.Builder(ARBITRARY_USER_ID) + .setUserConfigAllowed(true) + .setAutoDetectionSupported(true) + .setSystemClockUpdateThresholdMillis(ARBITRARY_SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS) + .setAutoSuggestionLowerBound(ARBITRARY_SUGGESTION_LOWER_BOUND) + .setManualSuggestionLowerBound(ARBITRARY_SUGGESTION_LOWER_BOUND) + .setSuggestionUpperBound(ARBITRARY_SUGGESTION_UPPER_BOUND) + .setOriginPriorities(ARBITRARY_ORIGIN_PRIORITIES) + .setAutoDetectionEnabledSetting(autoDetectionEnabled) + .build(); + } } 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 f776d3da0e79..4b417ba367ae 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -31,12 +31,14 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.app.time.ExternalTimeSuggestion; import android.app.time.ITimeDetectorListener; +import android.app.time.TimeCapabilitiesAndConfig; import android.app.time.TimeConfiguration; import android.app.time.TimeState; import android.app.time.UnixEpochTime; @@ -77,14 +79,14 @@ public class TimeDetectorServiceTest { private Context mMockContext; - private TimeDetectorService mTimeDetectorService; private HandlerThread mHandlerThread; private TestHandler mTestHandler; private TestCallerIdentityInjector mTestCallerIdentityInjector; - private FakeServiceConfigAccessor mFakeServiceConfigAccessor; - private NtpTrustedTime mMockNtpTrustedTime; - private FakeTimeDetectorStrategy mFakeTimeDetectorStrategy; + private FakeServiceConfigAccessor mFakeServiceConfigAccessorSpy; + private FakeTimeDetectorStrategy mFakeTimeDetectorStrategySpy; + private NtpTrustedTime mMockNtpTrustedTime; + private TimeDetectorService mTimeDetectorService; @Before public void setUp() { @@ -98,13 +100,13 @@ public class TimeDetectorServiceTest { mTestCallerIdentityInjector = new TestCallerIdentityInjector(); mTestCallerIdentityInjector.initializeCallingUserId(ARBITRARY_USER_ID); - mFakeTimeDetectorStrategy = new FakeTimeDetectorStrategy(); - mFakeServiceConfigAccessor = new FakeServiceConfigAccessor(); + mFakeServiceConfigAccessorSpy = spy(new FakeServiceConfigAccessor()); + mFakeTimeDetectorStrategySpy = spy(new FakeTimeDetectorStrategy()); mMockNtpTrustedTime = mock(NtpTrustedTime.class); mTimeDetectorService = new TimeDetectorService( - mMockContext, mTestHandler, mTestCallerIdentityInjector, mFakeServiceConfigAccessor, - mFakeTimeDetectorStrategy, mMockNtpTrustedTime); + mMockContext, mTestHandler, mTestCallerIdentityInjector, + mFakeServiceConfigAccessorSpy, mFakeTimeDetectorStrategySpy, mMockNtpTrustedTime); } @After @@ -129,13 +131,19 @@ public class TimeDetectorServiceTest { ConfigurationInternal configuration = createConfigurationInternal(true /* autoDetectionEnabled*/); - mFakeServiceConfigAccessor.initializeConfiguration(configuration); - - assertEquals(configuration.capabilitiesAndConfig(), - mTimeDetectorService.getCapabilitiesAndConfig()); + mFakeServiceConfigAccessorSpy.initializeConfiguration(configuration); + TimeCapabilitiesAndConfig actualCapabilitiesAndConfig = + mTimeDetectorService.getCapabilitiesAndConfig(); verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); + int expectedUserId = mTestCallerIdentityInjector.getCallingUserId(); + verify(mFakeServiceConfigAccessorSpy).getConfigurationInternal(expectedUserId); + + boolean bypassUserPolicyChecks = false; + TimeCapabilitiesAndConfig expectedCapabilitiesAndConfig = + configuration.createCapabilitiesAndConfig(bypassUserPolicyChecks); + assertEquals(expectedCapabilitiesAndConfig, actualCapabilitiesAndConfig); } @Test @@ -165,7 +173,7 @@ public class TimeDetectorServiceTest { public void testListenerRegistrationAndCallbacks() throws Exception { ConfigurationInternal initialConfiguration = createConfigurationInternal(false /* autoDetectionEnabled */); - mFakeServiceConfigAccessor.initializeConfiguration(initialConfiguration); + mFakeServiceConfigAccessorSpy.initializeConfiguration(initialConfiguration); IBinder mockListenerBinder = mock(IBinder.class); ITimeDetectorListener mockListener = mock(ITimeDetectorListener.class); @@ -258,32 +266,34 @@ public class TimeDetectorServiceTest { eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE), anyString()); mTestHandler.waitForMessagesToBeProcessed(); - mFakeTimeDetectorStrategy.verifySuggestTelephonyTimeCalled(timeSuggestion); + verify(mFakeTimeDetectorStrategySpy).suggestTelephonyTime(timeSuggestion); } @Test public void testSuggestManualTime_withoutPermission() { doThrow(new SecurityException("Mock")) - .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); + .when(mMockContext).enforceCallingPermission(anyString(), any()); ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion(); assertThrows(SecurityException.class, () -> mTimeDetectorService.suggestManualTime(manualTimeSuggestion)); - verify(mMockContext).enforceCallingOrSelfPermission( + verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), anyString()); } @Test public void testSuggestManualTime() throws Exception { - doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); + doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion(); assertTrue(mTimeDetectorService.suggestManualTime(manualTimeSuggestion)); - mFakeTimeDetectorStrategy.verifySuggestManualTimeCalled( - mTestCallerIdentityInjector.getCallingUserId(), manualTimeSuggestion); + int expectedUserId = mTestCallerIdentityInjector.getCallingUserId(); + boolean expectedBypassUserPolicyChecks = false; + verify(mFakeTimeDetectorStrategySpy).suggestManualTime( + expectedUserId, manualTimeSuggestion, expectedBypassUserPolicyChecks); - verify(mMockContext).enforceCallingOrSelfPermission( + verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), anyString()); } @@ -291,55 +301,55 @@ public class TimeDetectorServiceTest { @Test public void testSuggestNetworkTime_withoutPermission() { doThrow(new SecurityException("Mock")) - .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); + .when(mMockContext).enforceCallingPermission(anyString(), any()); NetworkTimeSuggestion networkTimeSuggestion = createNetworkTimeSuggestion(); assertThrows(SecurityException.class, () -> mTimeDetectorService.suggestNetworkTime(networkTimeSuggestion)); - verify(mMockContext).enforceCallingOrSelfPermission( + verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.SET_TIME), anyString()); } @Test public void testSuggestNetworkTime() throws Exception { - doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); + doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); NetworkTimeSuggestion networkTimeSuggestion = createNetworkTimeSuggestion(); mTimeDetectorService.suggestNetworkTime(networkTimeSuggestion); mTestHandler.assertTotalMessagesEnqueued(1); - verify(mMockContext).enforceCallingOrSelfPermission( + verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.SET_TIME), anyString()); mTestHandler.waitForMessagesToBeProcessed(); - mFakeTimeDetectorStrategy.verifySuggestNetworkTimeCalled(networkTimeSuggestion); + verify(mFakeTimeDetectorStrategySpy).suggestNetworkTime(networkTimeSuggestion); } @Test public void testSuggestGnssTime_withoutPermission() { doThrow(new SecurityException("Mock")) - .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); + .when(mMockContext).enforceCallingPermission(anyString(), any()); GnssTimeSuggestion gnssTimeSuggestion = createGnssTimeSuggestion(); assertThrows(SecurityException.class, () -> mTimeDetectorService.suggestGnssTime(gnssTimeSuggestion)); - verify(mMockContext).enforceCallingOrSelfPermission( + verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.SET_TIME), anyString()); } @Test public void testSuggestGnssTime() throws Exception { - doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); + doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); GnssTimeSuggestion gnssTimeSuggestion = createGnssTimeSuggestion(); mTimeDetectorService.suggestGnssTime(gnssTimeSuggestion); mTestHandler.assertTotalMessagesEnqueued(1); - verify(mMockContext).enforceCallingOrSelfPermission( + verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.SET_TIME), anyString()); mTestHandler.waitForMessagesToBeProcessed(); - mFakeTimeDetectorStrategy.verifySuggestGnssTimeCalled(gnssTimeSuggestion); + verify(mFakeTimeDetectorStrategySpy).suggestGnssTime(gnssTimeSuggestion); } @Test @@ -366,7 +376,7 @@ public class TimeDetectorServiceTest { eq(android.Manifest.permission.SUGGEST_EXTERNAL_TIME), anyString()); mTestHandler.waitForMessagesToBeProcessed(); - mFakeTimeDetectorStrategy.verifySuggestExternalTimeCalled(externalTimeSuggestion); + verify(mFakeTimeDetectorStrategySpy).suggestExternalTime(externalTimeSuggestion); } @Test @@ -390,7 +400,7 @@ public class TimeDetectorServiceTest { public void testGetTimeState() { doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); TimeState fakeState = new TimeState(new UnixEpochTime(12345L, 98765L), true); - mFakeTimeDetectorStrategy.setTimeState(fakeState); + mFakeTimeDetectorStrategySpy.setTimeState(fakeState); TimeState actualState = mTimeDetectorService.getTimeState(); @@ -418,7 +428,7 @@ public class TimeDetectorServiceTest { verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); - assertEquals(mFakeTimeDetectorStrategy.getTimeState(), state); + assertEquals(mFakeTimeDetectorStrategySpy.getTimeState(), state); } @Test @@ -442,7 +452,7 @@ public class TimeDetectorServiceTest { verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); - mFakeTimeDetectorStrategy.verifyConfirmTimeCalled(confirmationTime); + verify(mFakeTimeDetectorStrategySpy).confirmTime(confirmationTime); } @Test @@ -467,8 +477,10 @@ public class TimeDetectorServiceTest { mTimeDetectorService.setManualTime(timeSuggestion)); // The service calls "suggestManualTime()" because the logic is the same. - mFakeTimeDetectorStrategy.verifySuggestManualTimeCalled( - mTestCallerIdentityInjector.getCallingUserId(), timeSuggestion); + int expectedUserId = mTestCallerIdentityInjector.getCallingUserId(); + boolean expectedBypassUserPolicyChecks = false; + verify(mFakeTimeDetectorStrategySpy).suggestManualTime( + expectedUserId, timeSuggestion, expectedBypassUserPolicyChecks); verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); @@ -495,7 +507,7 @@ public class TimeDetectorServiceTest { mTimeDetectorService.dump(null, pw, null); verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP)); - mFakeTimeDetectorStrategy.verifyDumpCalled(); + verify(mFakeTimeDetectorStrategySpy).dump(any(), any()); } private static TimeConfiguration createTimeConfiguration(boolean autoDetectionEnabled) { diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index 08632284cfc8..62dae481ac0b 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -38,8 +38,6 @@ import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.TelephonyTimeSuggestion; import android.os.TimestampedValue; -import androidx.test.runner.AndroidJUnit4; - import com.android.server.SystemClockTime.TimeConfidence; import com.android.server.timedetector.TimeDetectorStrategy.Origin; import com.android.server.timezonedetector.ConfigurationChangeListener; @@ -55,7 +53,10 @@ import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Objects; -@RunWith(AndroidJUnit4.class) +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + +@RunWith(JUnitParamsRunner.class) public class TimeDetectorStrategyImplTest { private static final @UserIdInt int ARBITRARY_USER_ID = 9876; @@ -656,12 +657,45 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis = script.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); + boolean bypassUserPolicyChecks = false; + boolean expectedResult = true; script.simulateManualTimeSuggestion( - ARBITRARY_USER_ID, timeSuggestion, true /* expectedResult */) + ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult) .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); } + /** Confirms the effect of user policy restrictions on being able to set the time. */ + @Test + @Parameters({ "true,true", "true,false", "false,true", "false,false" }) + public void testSuggestManualTime_autoTimeDisabled_userRestrictions( + boolean userConfigAllowed, boolean bypassUserPolicyChecks) { + ConfigurationInternal configAutoDisabled = + new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED) + .setUserConfigAllowed(userConfigAllowed) + .build(); + Script script = new Script().simulateConfigurationInternalChange(configAutoDisabled) + .verifySystemClockConfidence(TIME_CONFIDENCE_LOW); + + ManualTimeSuggestion timeSuggestion = + script.generateManualTimeSuggestion(ARBITRARY_TEST_TIME); + + script.simulateTimePassing(); + + long expectedSystemClockMillis = + script.calculateTimeInMillisForNow(timeSuggestion.getUnixEpochTime()); + boolean expectedResult = userConfigAllowed || bypassUserPolicyChecks; + script.simulateManualTimeSuggestion( + ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult); + if (expectedResult) { + script.verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) + .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis); + } else { + script.verifySystemClockConfidence(TIME_CONFIDENCE_LOW) + .verifySystemClockWasNotSetAndResetCallTracking(); + } + } + @Test public void testSuggestManualTime_retainsAutoSignal() { Script script = new Script().simulateConfigurationInternalChange(CONFIG_AUTO_ENABLED) @@ -704,8 +738,10 @@ public class TimeDetectorStrategyImplTest { long expectedManualClockMillis = script.calculateTimeInMillisForNow(manualTimeSuggestion.getUnixEpochTime()); + boolean bypassUserPolicyChecks = false; + boolean expectedResult = true; script.simulateManualTimeSuggestion( - ARBITRARY_USER_ID, manualTimeSuggestion, true /* expectedResult */) + ARBITRARY_USER_ID, manualTimeSuggestion, bypassUserPolicyChecks, expectedResult) .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(expectedManualClockMillis) .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); @@ -737,9 +773,10 @@ public class TimeDetectorStrategyImplTest { ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(ARBITRARY_TEST_TIME); - script.simulateTimePassing() - .simulateManualTimeSuggestion( - ARBITRARY_USER_ID, timeSuggestion, false /* expectedResult */) + boolean bypassUserPolicyChecks = false; + boolean expectedResult = false; + script.simulateTimePassing().simulateManualTimeSuggestion( + ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult) .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -755,8 +792,10 @@ public class TimeDetectorStrategyImplTest { Instant aboveUpperBound = TEST_SUGGESTION_UPPER_BOUND.plusSeconds(1); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(aboveUpperBound); + boolean bypassUserPolicyChecks = false; + boolean expectedResult = false; script.simulateManualTimeSuggestion( - ARBITRARY_USER_ID, timeSuggestion, false /* expectedResult */) + ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult) .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -772,8 +811,10 @@ public class TimeDetectorStrategyImplTest { Instant belowUpperBound = TEST_SUGGESTION_UPPER_BOUND.minusSeconds(1); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(belowUpperBound); + boolean bypassUserPolicyChecks = false; + boolean expectedResult = true; script.simulateManualTimeSuggestion( - ARBITRARY_USER_ID, timeSuggestion, true /* expectedResult */) + ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult) .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(belowUpperBound.toEpochMilli()); } @@ -789,8 +830,10 @@ public class TimeDetectorStrategyImplTest { Instant belowLowerBound = TEST_SUGGESTION_LOWER_BOUND.minusSeconds(1); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(belowLowerBound); + boolean bypassUserPolicyChecks = false; + boolean expectedResult = false; script.simulateManualTimeSuggestion( - ARBITRARY_USER_ID, timeSuggestion, false /* expectedResult */) + ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult) .verifySystemClockConfidence(TIME_CONFIDENCE_LOW) .verifySystemClockWasNotSetAndResetCallTracking(); } @@ -806,8 +849,10 @@ public class TimeDetectorStrategyImplTest { Instant aboveLowerBound = TEST_SUGGESTION_LOWER_BOUND.plusSeconds(1); ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(aboveLowerBound); + boolean bypassUserPolicyChecks = false; + boolean expectedResult = true; script.simulateManualTimeSuggestion( - ARBITRARY_USER_ID, timeSuggestion, true /* expectedResult */) + ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult) .verifySystemClockConfidence(TIME_CONFIDENCE_HIGH) .verifySystemClockWasSetAndResetCallTracking(aboveLowerBound.toEpochMilli()); } @@ -1758,8 +1803,10 @@ public class TimeDetectorStrategyImplTest { ManualTimeSuggestion timeSuggestion = script.generateManualTimeSuggestion(ARBITRARY_TEST_TIME); + boolean bypassUserPolicyChecks = false; + boolean expectedResult = true; script.simulateManualTimeSuggestion( - ARBITRARY_USER_ID, timeSuggestion, true /* expectedResult */) + ARBITRARY_USER_ID, timeSuggestion, bypassUserPolicyChecks, expectedResult) .verifySystemClockWasSetAndResetCallTracking(ARBITRARY_TEST_TIME.toEpochMilli()); } @@ -1951,14 +1998,15 @@ public class TimeDetectorStrategyImplTest { Script simulateManualTimeSuggestion( @UserIdInt int userId, ManualTimeSuggestion timeSuggestion, - boolean expectedResult) { + boolean bypassUserPolicyChecks, boolean expectedResult) { String errorMessage = expectedResult ? "Manual time suggestion was ignored, but expected to be accepted." : "Manual time suggestion was accepted, but expected to be ignored."; assertEquals( errorMessage, expectedResult, - mTimeDetectorStrategy.suggestManualTime(userId, timeSuggestion)); + mTimeDetectorStrategy.suggestManualTime( + userId, timeSuggestion, bypassUserPolicyChecks)); return this; } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java index 810bd82f4431..7140097bb6c0 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java @@ -29,27 +29,36 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.app.time.TimeCapabilitiesAndConfig; import android.app.time.TimeZoneCapabilities; import android.app.time.TimeZoneCapabilitiesAndConfig; import android.app.time.TimeZoneConfiguration; import org.junit.Test; +import org.junit.runner.RunWith; + +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; /** - * Tests for {@link ConfigurationInternal} and the {@link TimeZoneCapabilitiesAndConfig}. + * Tests for {@link ConfigurationInternal} and associated {@link TimeZoneCapabilitiesAndConfig} + * behavior. */ +@RunWith(JUnitParamsRunner.class) public class ConfigurationInternalTest { private static final int ARBITRARY_USER_ID = 99999; /** - * Tests when {@link ConfigurationInternal#isUserConfigAllowed()} and - * {@link ConfigurationInternal#isAutoDetectionSupported()} are both true. + * Tests {@link TimeCapabilitiesAndConfig} behavior in different scenarios when auto detection + * is supported (and geo detection is supported) */ @Test - public void test_unrestricted() { + @Parameters({ "true,true", "true,false", "false,true", "false,false" }) + public void test_autoDetectionSupported_capabilitiesAndConfiguration( + boolean userConfigAllowed, boolean bypassUserPolicyChecks) { ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) - .setUserConfigAllowed(true) + .setUserConfigAllowed(userConfigAllowed) .setTelephonyDetectionFeatureSupported(true) .setGeoDetectionFeatureSupported(true) .setGeoDetectionRunInBackgroundEnabled(false) @@ -59,73 +68,10 @@ public class ConfigurationInternalTest { .setLocationEnabledSetting(true) .setGeoDetectionEnabledSetting(true) .build(); - { - ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig) - .setAutoDetectionEnabledSetting(true) - .build(); - assertTrue(autoOnConfig.getAutoDetectionEnabledSetting()); - assertTrue(autoOnConfig.getGeoDetectionEnabledSetting()); - assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior()); - assertTrue(autoOnConfig.isGeoDetectionExecutionEnabled()); - assertEquals(DETECTION_MODE_GEO, autoOnConfig.getDetectionMode()); - - TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = - autoOnConfig.createCapabilitiesAndConfig(); - TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); - assertEquals(CAPABILITY_POSSESSED, - capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_NOT_APPLICABLE, - capabilities.getSetManualTimeZoneCapability()); - assertEquals(CAPABILITY_POSSESSED, - capabilities.getConfigureGeoDetectionEnabledCapability()); - - TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration(); - assertTrue(configuration.isAutoDetectionEnabled()); - assertTrue(configuration.isGeoDetectionEnabled()); - } + boolean userRestrictionsExpected = !(userConfigAllowed || bypassUserPolicyChecks); - { - ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig) - .setAutoDetectionEnabledSetting(false) - .build(); - assertFalse(autoOffConfig.getAutoDetectionEnabledSetting()); - assertTrue(autoOffConfig.getGeoDetectionEnabledSetting()); - assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior()); - assertFalse(autoOffConfig.isGeoDetectionExecutionEnabled()); - assertEquals(DETECTION_MODE_MANUAL, autoOffConfig.getDetectionMode()); - - TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = - autoOffConfig.createCapabilitiesAndConfig(); - - TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); - assertEquals(CAPABILITY_POSSESSED, - capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_POSSESSED, - capabilities.getSetManualTimeZoneCapability()); - assertEquals(CAPABILITY_NOT_APPLICABLE, - capabilities.getConfigureGeoDetectionEnabledCapability()); - - TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration(); - assertFalse(configuration.isAutoDetectionEnabled()); - assertTrue(configuration.isGeoDetectionEnabled()); - } - } - - /** Tests when {@link ConfigurationInternal#isUserConfigAllowed()} is false */ - @Test - public void test_restricted() { - ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) - .setUserConfigAllowed(false) - .setTelephonyDetectionFeatureSupported(true) - .setGeoDetectionFeatureSupported(true) - .setTelephonyFallbackSupported(false) - .setGeoDetectionRunInBackgroundEnabled(false) - .setEnhancedMetricsCollectionEnabled(false) - .setAutoDetectionEnabledSetting(true) - .setLocationEnabledSetting(true) - .setGeoDetectionEnabledSetting(true) - .build(); + // Auto-detection enabled. { ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig) .setAutoDetectionEnabledSetting(true) @@ -137,13 +83,20 @@ public class ConfigurationInternalTest { assertEquals(DETECTION_MODE_GEO, autoOnConfig.getDetectionMode()); TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = - autoOnConfig.createCapabilitiesAndConfig(); + autoOnConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); - assertEquals(CAPABILITY_NOT_ALLOWED, - capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_NOT_ALLOWED, - capabilities.getSetManualTimeZoneCapability()); + if (userRestrictionsExpected) { + assertEquals(CAPABILITY_NOT_ALLOWED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_ALLOWED, + capabilities.getSetManualTimeZoneCapability()); + } else { + assertEquals(CAPABILITY_POSSESSED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_APPLICABLE, + capabilities.getSetManualTimeZoneCapability()); + } // This has user privacy implications so it is not restricted in the same way as others. assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabledCapability()); @@ -153,6 +106,7 @@ public class ConfigurationInternalTest { assertTrue(configuration.isGeoDetectionEnabled()); } + // Auto-detection disabled. { ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig) .setAutoDetectionEnabledSetting(false) @@ -164,13 +118,20 @@ public class ConfigurationInternalTest { assertEquals(DETECTION_MODE_MANUAL, autoOffConfig.getDetectionMode()); TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = - autoOffConfig.createCapabilitiesAndConfig(); + autoOffConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); - assertEquals(CAPABILITY_NOT_ALLOWED, - capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_NOT_ALLOWED, - capabilities.getSetManualTimeZoneCapability()); + if (userRestrictionsExpected) { + assertEquals(CAPABILITY_NOT_ALLOWED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_ALLOWED, + capabilities.getSetManualTimeZoneCapability()); + } else { + assertEquals(CAPABILITY_POSSESSED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_POSSESSED, + capabilities.getSetManualTimeZoneCapability()); + } // This has user privacy implications so it is not restricted in the same way as others. assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getConfigureGeoDetectionEnabledCapability()); @@ -181,11 +142,16 @@ public class ConfigurationInternalTest { } } - /** Tests when {@link ConfigurationInternal#isAutoDetectionSupported()} is false. */ + /** + * Tests {@link TimeCapabilitiesAndConfig} behavior in different scenarios when auto detection + * is not supported. + */ @Test - public void test_autoDetectNotSupported() { + @Parameters({ "true,true", "true,false", "false,true", "false,false" }) + public void test_autoDetectNotSupported_capabilitiesAndConfiguration( + boolean userConfigAllowed, boolean bypassUserPolicyChecks) { ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) - .setUserConfigAllowed(true) + .setUserConfigAllowed(userConfigAllowed) .setTelephonyDetectionFeatureSupported(false) .setGeoDetectionFeatureSupported(false) .setGeoDetectionRunInBackgroundEnabled(false) @@ -195,6 +161,10 @@ public class ConfigurationInternalTest { .setLocationEnabledSetting(true) .setGeoDetectionEnabledSetting(true) .build(); + + boolean userRestrictionsExpected = !(userConfigAllowed || bypassUserPolicyChecks); + + // Auto-detection enabled. { ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig) .setAutoDetectionEnabledSetting(true) @@ -206,12 +176,16 @@ public class ConfigurationInternalTest { assertEquals(DETECTION_MODE_MANUAL, autoOnConfig.getDetectionMode()); TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = - autoOnConfig.createCapabilitiesAndConfig(); + autoOnConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeZoneCapability()); + if (userRestrictionsExpected) { + assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeZoneCapability()); + } else { + assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeZoneCapability()); + } assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabledCapability()); @@ -219,6 +193,8 @@ public class ConfigurationInternalTest { assertTrue(configuration.isAutoDetectionEnabled()); assertTrue(configuration.isGeoDetectionEnabled()); } + + // Auto-detection disabled. { ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig) .setAutoDetectionEnabledSetting(false) @@ -230,12 +206,16 @@ public class ConfigurationInternalTest { assertEquals(DETECTION_MODE_MANUAL, autoOffConfig.getDetectionMode()); TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = - autoOffConfig.createCapabilitiesAndConfig(); + autoOffConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeZoneCapability()); + if (userRestrictionsExpected) { + assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeZoneCapability()); + } else { + assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeZoneCapability()); + } assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabledCapability()); @@ -246,13 +226,15 @@ public class ConfigurationInternalTest { } /** - * Tests when {@link ConfigurationInternal#isAutoDetectionSupported()} is true, but - * {@link ConfigurationInternal#isGeoDetectionSupported()} is false. + * Tests {@link TimeCapabilitiesAndConfig} behavior in different scenarios when auto detection + * is supported (and geo detection is not supported). */ @Test - public void test_geoDetectNotSupported() { + @Parameters({ "true,true", "true,false", "false,true", "false,false" }) + public void test_geoDetectNotSupported_capabilitiesAndConfiguration( + boolean userConfigAllowed, boolean bypassUserPolicyChecks) { ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID) - .setUserConfigAllowed(true) + .setUserConfigAllowed(userConfigAllowed) .setTelephonyDetectionFeatureSupported(true) .setGeoDetectionFeatureSupported(false) .setGeoDetectionRunInBackgroundEnabled(false) @@ -262,6 +244,10 @@ public class ConfigurationInternalTest { .setLocationEnabledSetting(true) .setGeoDetectionEnabledSetting(true) .build(); + + boolean userRestrictionsExpected = !(userConfigAllowed || bypassUserPolicyChecks); + + // Auto-detection enabled. { ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig) .setAutoDetectionEnabledSetting(true) @@ -273,13 +259,20 @@ public class ConfigurationInternalTest { assertEquals(DETECTION_MODE_TELEPHONY, autoOnConfig.getDetectionMode()); TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = - autoOnConfig.createCapabilitiesAndConfig(); + autoOnConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); - assertEquals(CAPABILITY_POSSESSED, - capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_NOT_APPLICABLE, - capabilities.getSetManualTimeZoneCapability()); + if (userRestrictionsExpected) { + assertEquals(CAPABILITY_NOT_ALLOWED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_ALLOWED, + capabilities.getSetManualTimeZoneCapability()); + } else { + assertEquals(CAPABILITY_POSSESSED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_APPLICABLE, + capabilities.getSetManualTimeZoneCapability()); + } assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabledCapability()); @@ -287,6 +280,8 @@ public class ConfigurationInternalTest { assertTrue(configuration.isAutoDetectionEnabled()); assertTrue(configuration.isGeoDetectionEnabled()); } + + // Auto-detection disabled. { ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig) .setAutoDetectionEnabledSetting(false) @@ -298,12 +293,18 @@ public class ConfigurationInternalTest { assertEquals(DETECTION_MODE_MANUAL, autoOffConfig.getDetectionMode()); TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = - autoOffConfig.createCapabilitiesAndConfig(); + autoOffConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); - assertEquals(CAPABILITY_POSSESSED, - capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeZoneCapability()); + if (userRestrictionsExpected) { + assertEquals(CAPABILITY_NOT_ALLOWED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeZoneCapability()); + } else { + assertEquals(CAPABILITY_POSSESSED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_POSSESSED, capabilities.getSetManualTimeZoneCapability()); + } assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabledCapability()); diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java index a97ad8c57a0d..fdee86ea1285 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java @@ -32,7 +32,7 @@ import java.util.List; import java.util.Optional; /** A partially implemented, fake implementation of ServiceConfigAccessor for tests. */ -class FakeServiceConfigAccessor implements ServiceConfigAccessor { +public class FakeServiceConfigAccessor implements ServiceConfigAccessor { private final List<ConfigurationChangeListener> mConfigurationInternalChangeListeners = new ArrayList<>(); @@ -55,7 +55,8 @@ class FakeServiceConfigAccessor implements ServiceConfigAccessor { @Override public boolean updateConfiguration( - @UserIdInt int userID, @NonNull TimeZoneConfiguration requestedChanges) { + @UserIdInt int userID, @NonNull TimeZoneConfiguration requestedChanges, + boolean bypassUserPolicyChecks) { assertNotNull(mConfigurationInternal); assertNotNull(requestedChanges); @@ -63,7 +64,7 @@ class FakeServiceConfigAccessor implements ServiceConfigAccessor { // old configuration merged with the new if the user has the capability to up the settings. // Then, if the configuration changed, the change listener is invoked. TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = - mConfigurationInternal.createCapabilitiesAndConfig(); + mConfigurationInternal.createCapabilitiesAndConfig(bypassUserPolicyChecks); TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration(); TimeZoneConfiguration newConfiguration = diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java index 339e5b23cd9a..228dc952356a 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java @@ -15,9 +15,6 @@ */ package com.android.server.timezonedetector; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.time.TimeZoneState; @@ -25,21 +22,12 @@ import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.util.IndentingPrintWriter; -class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { +public class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { private TimeZoneState mTimeZoneState; - // Call tracking. - private GeolocationTimeZoneSuggestion mLastGeolocationSuggestion; - private ManualTimeZoneSuggestion mLastManualSuggestion; - private Integer mLastManualSuggestionUserId; - private TelephonyTimeZoneSuggestion mLastTelephonySuggestion; - private String mLastConfirmedTimeZone; - private boolean mDumpCalled; - @Override public boolean confirmTimeZone(String timeZoneId) { - mLastConfirmedTimeZone = timeZoneId; return false; } @@ -55,21 +43,17 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { @Override public void suggestGeolocationTimeZone(GeolocationTimeZoneSuggestion timeZoneSuggestion) { - mLastGeolocationSuggestion = timeZoneSuggestion; } @Override public boolean suggestManualTimeZone( - @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion timeZoneSuggestion) { - mLastManualSuggestionUserId = userId; - mLastManualSuggestion = timeZoneSuggestion; + @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion timeZoneSuggestion, + boolean bypassUserPolicyChecks) { return true; } @Override - public void suggestTelephonyTimeZone( - @NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion) { - mLastTelephonySuggestion = timeZoneSuggestion; + public void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion) { } @Override @@ -94,38 +78,5 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { @Override public void dump(IndentingPrintWriter pw, String[] args) { - mDumpCalled = true; - } - - void resetCallTracking() { - mLastGeolocationSuggestion = null; - mLastManualSuggestion = null; - mLastManualSuggestionUserId = null; - mLastTelephonySuggestion = null; - mDumpCalled = false; - mLastConfirmedTimeZone = null; - } - - void verifySuggestGeolocationTimeZoneCalled( - GeolocationTimeZoneSuggestion expectedSuggestion) { - assertEquals(expectedSuggestion, mLastGeolocationSuggestion); - } - - void verifySuggestManualTimeZoneCalled( - @UserIdInt int expectedUserId, ManualTimeZoneSuggestion expectedSuggestion) { - assertEquals((Integer) expectedUserId, mLastManualSuggestionUserId); - assertEquals(expectedSuggestion, mLastManualSuggestion); - } - - void verifySuggestTelephonyTimeZoneCalled(TelephonyTimeZoneSuggestion expectedSuggestion) { - assertEquals(expectedSuggestion, mLastTelephonySuggestion); - } - - void verifyDumpCalled() { - assertTrue(mDumpCalled); - } - - void verifyConfirmTimeZoneCalled(String expectedTimeZoneId) { - assertEquals(expectedTimeZoneId, mLastConfirmedTimeZone); } } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java new file mode 100644 index 000000000000..aad06d87f111 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java @@ -0,0 +1,34 @@ +/* + * Copyright 2022 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 com.android.server.timezonedetector; + +import android.annotation.UserIdInt; + +/** A fake {@link CurrentUserIdentityInjector} used in tests. */ +public class TestCurrentUserIdentityInjector implements CurrentUserIdentityInjector { + + private Integer mCurrentUserId; + + public void initializeCurrentUserId(@UserIdInt int userId) { + mCurrentUserId = userId; + } + + @Override + public int getCurrentUserId() { + return mCurrentUserId; + } +} diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java index c5bab760fcfe..276fdb971172 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java @@ -16,8 +16,15 @@ package com.android.server.timezonedetector; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import android.app.time.TimeZoneCapabilitiesAndConfig; +import android.app.time.TimeZoneConfiguration; +import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.content.Context; import android.os.HandlerThread; @@ -35,15 +42,18 @@ import java.util.List; public class TimeZoneDetectorInternalImplTest { private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234L; - private static final List<String> ARBITRARY_ZONE_IDS = Arrays.asList("TestZoneId"); + private static final String ARBITRARY_ZONE_ID = "TestZoneId"; + private static final List<String> ARBITRARY_ZONE_IDS = Arrays.asList(ARBITRARY_ZONE_ID); + private static final int ARBITRARY_USER_ID = 9999; private Context mMockContext; - private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategy; - - private TimeZoneDetectorInternalImpl mTimeZoneDetectorInternal; private HandlerThread mHandlerThread; private TestHandler mTestHandler; + private TestCurrentUserIdentityInjector mTestCurrentUserIdentityInjector; + private FakeServiceConfigAccessor mFakeServiceConfigAccessorSpy; + private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategySpy; + private TimeZoneDetectorInternalImpl mTimeZoneDetectorInternal; @Before public void setUp() { @@ -53,11 +63,14 @@ public class TimeZoneDetectorInternalImplTest { mHandlerThread = new HandlerThread("TimeZoneDetectorInternalTest"); mHandlerThread.start(); mTestHandler = new TestHandler(mHandlerThread.getLooper()); - - mFakeTimeZoneDetectorStrategy = new FakeTimeZoneDetectorStrategy(); + mTestCurrentUserIdentityInjector = new TestCurrentUserIdentityInjector(); + mTestCurrentUserIdentityInjector.initializeCurrentUserId(ARBITRARY_USER_ID); + mFakeServiceConfigAccessorSpy = spy(new FakeServiceConfigAccessor()); + mFakeTimeZoneDetectorStrategySpy = spy(new FakeTimeZoneDetectorStrategy()); mTimeZoneDetectorInternal = new TimeZoneDetectorInternalImpl( - mMockContext, mTestHandler, mFakeTimeZoneDetectorStrategy); + mMockContext, mTestHandler, mTestCurrentUserIdentityInjector, + mFakeServiceConfigAccessorSpy, mFakeTimeZoneDetectorStrategySpy); } @After @@ -67,17 +80,84 @@ public class TimeZoneDetectorInternalImplTest { } @Test + public void testGetCapabilitiesAndConfigForDpm() throws Exception { + final boolean autoDetectionEnabled = true; + ConfigurationInternal testConfig = createConfigurationInternal(autoDetectionEnabled); + mFakeServiceConfigAccessorSpy.initializeConfiguration(testConfig); + + TimeZoneCapabilitiesAndConfig actualCapabilitiesAndConfig = + mTimeZoneDetectorInternal.getCapabilitiesAndConfigForDpm(); + + int expectedUserId = mTestCurrentUserIdentityInjector.getCurrentUserId(); + verify(mFakeServiceConfigAccessorSpy).getConfigurationInternal(expectedUserId); + + final boolean bypassUserPolicyChecks = true; + TimeZoneCapabilitiesAndConfig expectedCapabilitiesAndConfig = + testConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); + assertEquals(expectedCapabilitiesAndConfig, actualCapabilitiesAndConfig); + } + + @Test + public void testUpdateConfigurationForDpm() throws Exception { + final boolean autoDetectionEnabled = false; + ConfigurationInternal initialConfigurationInternal = + createConfigurationInternal(autoDetectionEnabled); + mFakeServiceConfigAccessorSpy.initializeConfiguration(initialConfigurationInternal); + + TimeZoneConfiguration timeConfiguration = new TimeZoneConfiguration.Builder() + .setAutoDetectionEnabled(true) + .build(); + assertTrue(mTimeZoneDetectorInternal.updateConfigurationForDpm(timeConfiguration)); + + final boolean expectedBypassUserPolicyChecks = true; + verify(mFakeServiceConfigAccessorSpy).updateConfiguration( + mTestCurrentUserIdentityInjector.getCurrentUserId(), + timeConfiguration, + expectedBypassUserPolicyChecks); + } + + @Test + public void testSetManualTimeZoneForDpm() throws Exception { + ManualTimeZoneSuggestion timeZoneSuggestion = createManualTimeZoneSuggestion(); + + // The fake strategy always returns true. + assertTrue(mTimeZoneDetectorInternal.setManualTimeZoneForDpm(timeZoneSuggestion)); + + int expectedUserId = mTestCurrentUserIdentityInjector.getCurrentUserId(); + boolean expectedBypassUserPolicyChecks = true; + verify(mFakeTimeZoneDetectorStrategySpy).suggestManualTimeZone( + expectedUserId, timeZoneSuggestion, expectedBypassUserPolicyChecks); + } + + @Test public void testSuggestGeolocationTimeZone() throws Exception { GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion(); mTimeZoneDetectorInternal.suggestGeolocationTimeZone(timeZoneSuggestion); mTestHandler.assertTotalMessagesEnqueued(1); mTestHandler.waitForMessagesToBeProcessed(); - mFakeTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion); + verify(mFakeTimeZoneDetectorStrategySpy).suggestGeolocationTimeZone(timeZoneSuggestion); + } + private static ManualTimeZoneSuggestion createManualTimeZoneSuggestion() { + return new ManualTimeZoneSuggestion(ARBITRARY_ZONE_ID); } private static GeolocationTimeZoneSuggestion createGeolocationTimeZoneSuggestion() { return GeolocationTimeZoneSuggestion.createCertainSuggestion( ARBITRARY_ELAPSED_REALTIME_MILLIS, ARBITRARY_ZONE_IDS); } + + private static ConfigurationInternal createConfigurationInternal(boolean autoDetectionEnabled) { + return new ConfigurationInternal.Builder(ARBITRARY_USER_ID) + .setTelephonyDetectionFeatureSupported(true) + .setGeoDetectionFeatureSupported(true) + .setTelephonyFallbackSupported(false) + .setGeoDetectionRunInBackgroundEnabled(false) + .setEnhancedMetricsCollectionEnabled(false) + .setUserConfigAllowed(true) + .setAutoDetectionEnabledSetting(autoDetectionEnabled) + .setLocationEnabledSetting(false) + .setGeoDetectionEnabledSetting(false) + .build(); + } } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java index fc6459725978..bb9d564b5aad 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java @@ -28,11 +28,13 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.app.time.ITimeZoneDetectorListener; +import android.app.time.TimeZoneCapabilitiesAndConfig; import android.app.time.TimeZoneConfiguration; import android.app.time.TimeZoneState; import android.app.timezonedetector.ManualTimeZoneSuggestion; @@ -67,8 +69,8 @@ public class TimeZoneDetectorServiceTest { private HandlerThread mHandlerThread; private TestHandler mTestHandler; private TestCallerIdentityInjector mTestCallerIdentityInjector; - private FakeServiceConfigAccessor mFakeServiceConfigAccessor; - private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategy; + private FakeServiceConfigAccessor mFakeServiceConfigAccessorSpy; + private FakeTimeZoneDetectorStrategy mFakeTimeZoneDetectorStrategySpy; @Before @@ -83,12 +85,12 @@ public class TimeZoneDetectorServiceTest { mTestCallerIdentityInjector = new TestCallerIdentityInjector(); mTestCallerIdentityInjector.initializeCallingUserId(ARBITRARY_USER_ID); - mFakeTimeZoneDetectorStrategy = new FakeTimeZoneDetectorStrategy(); - mFakeServiceConfigAccessor = new FakeServiceConfigAccessor(); + mFakeServiceConfigAccessorSpy = spy(new FakeServiceConfigAccessor()); + mFakeTimeZoneDetectorStrategySpy = spy(new FakeTimeZoneDetectorStrategy()); mTimeZoneDetectorService = new TimeZoneDetectorService( mMockContext, mTestHandler, mTestCallerIdentityInjector, - mFakeServiceConfigAccessor, mFakeTimeZoneDetectorStrategy); + mFakeServiceConfigAccessorSpy, mFakeTimeZoneDetectorStrategySpy); } @After @@ -113,13 +115,21 @@ public class TimeZoneDetectorServiceTest { ConfigurationInternal configuration = createConfigurationInternal(true /* autoDetectionEnabled*/); - mFakeServiceConfigAccessor.initializeConfiguration(configuration); + mFakeServiceConfigAccessorSpy.initializeConfiguration(configuration); - assertEquals(configuration.createCapabilitiesAndConfig(), - mTimeZoneDetectorService.getCapabilitiesAndConfig()); + TimeZoneCapabilitiesAndConfig actualCapabilitiesAndConfig = + mTimeZoneDetectorService.getCapabilitiesAndConfig(); verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); + + int expectedUserId = mTestCallerIdentityInjector.getCallingUserId(); + verify(mFakeServiceConfigAccessorSpy).getConfigurationInternal(expectedUserId); + + boolean expectedBypassUserPolicyChecks = false; + TimeZoneCapabilitiesAndConfig expectedCapabilitiesAndConfig = + configuration.createCapabilitiesAndConfig(expectedBypassUserPolicyChecks); + assertEquals(expectedCapabilitiesAndConfig, actualCapabilitiesAndConfig); } @Test @@ -150,7 +160,7 @@ public class TimeZoneDetectorServiceTest { public void testListenerRegistrationAndCallbacks() throws Exception { ConfigurationInternal initialConfiguration = createConfigurationInternal(false /* autoDetectionEnabled */); - mFakeServiceConfigAccessor.initializeConfiguration(initialConfiguration); + mFakeServiceConfigAccessorSpy.initializeConfiguration(initialConfiguration); IBinder mockListenerBinder = mock(IBinder.class); ITimeZoneDetectorListener mockListener = mock(ITimeZoneDetectorListener.class); @@ -222,46 +232,46 @@ public class TimeZoneDetectorServiceTest { @Test public void testSuggestGeolocationTimeZone_withoutPermission() { doThrow(new SecurityException("Mock")) - .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); + .when(mMockContext).enforceCallingPermission(anyString(), any()); GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion(); assertThrows(SecurityException.class, () -> mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion)); - verify(mMockContext).enforceCallingOrSelfPermission( + verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.SET_TIME_ZONE), anyString()); } @Test public void testSuggestGeolocationTimeZone() throws Exception { - doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); + doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion(); mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion); mTestHandler.assertTotalMessagesEnqueued(1); - verify(mMockContext).enforceCallingOrSelfPermission( + verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.SET_TIME_ZONE), anyString()); mTestHandler.waitForMessagesToBeProcessed(); - mFakeTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion); + verify(mFakeTimeZoneDetectorStrategySpy).suggestGeolocationTimeZone(timeZoneSuggestion); } @Test public void testSuggestManualTimeZone_withoutPermission() { doThrow(new SecurityException("Mock")) - .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); + .when(mMockContext).enforceCallingPermission(anyString(), any()); ManualTimeZoneSuggestion timeZoneSuggestion = createManualTimeZoneSuggestion(); assertThrows(SecurityException.class, () -> mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion)); - verify(mMockContext).enforceCallingOrSelfPermission( + verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), anyString()); } @Test public void testSuggestManualTimeZone() throws Exception { - doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any()); + doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); ManualTimeZoneSuggestion timeZoneSuggestion = createManualTimeZoneSuggestion(); @@ -269,10 +279,12 @@ public class TimeZoneDetectorServiceTest { assertEquals(expectedResult, mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion)); - mFakeTimeZoneDetectorStrategy.verifySuggestManualTimeZoneCalled( - mTestCallerIdentityInjector.getCallingUserId(), timeZoneSuggestion); + int expectedUserId = mTestCallerIdentityInjector.getCallingUserId(); + boolean expectedBypassUserPolicyChecks = false; + verify(mFakeTimeZoneDetectorStrategySpy).suggestManualTimeZone( + expectedUserId, timeZoneSuggestion, expectedBypassUserPolicyChecks); - verify(mMockContext).enforceCallingOrSelfPermission( + verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), anyString()); } @@ -312,20 +324,20 @@ public class TimeZoneDetectorServiceTest { eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE), anyString()); mTestHandler.waitForMessagesToBeProcessed(); - mFakeTimeZoneDetectorStrategy.verifySuggestTelephonyTimeZoneCalled(timeZoneSuggestion); + verify(mFakeTimeZoneDetectorStrategySpy).suggestTelephonyTimeZone(timeZoneSuggestion); } @Test public void testGetTimeZoneState() { doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); TimeZoneState fakeState = new TimeZoneState("Europe/Narnia", true); - mFakeTimeZoneDetectorStrategy.setTimeZoneState(fakeState); + mFakeTimeZoneDetectorStrategySpy.setTimeZoneState(fakeState); TimeZoneState actualState = mTimeZoneDetectorService.getTimeZoneState(); verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); - assertEquals(actualState, fakeState); + assertEquals(fakeState, actualState); } @Test @@ -347,7 +359,7 @@ public class TimeZoneDetectorServiceTest { verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); - assertEquals(mFakeTimeZoneDetectorStrategy.getTimeZoneState(), state); + assertEquals(state, mFakeTimeZoneDetectorStrategySpy.getTimeZoneState()); } @Test @@ -371,7 +383,7 @@ public class TimeZoneDetectorServiceTest { verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); - mFakeTimeZoneDetectorStrategy.verifyConfirmTimeZoneCalled("Europe/Narnia"); + verify(mFakeTimeZoneDetectorStrategySpy).confirmTimeZone("Europe/Narnia"); } @Test @@ -396,8 +408,10 @@ public class TimeZoneDetectorServiceTest { mTimeZoneDetectorService.setManualTimeZone(timeZoneSuggestion)); // The service calls "suggestManualTimeZone()" because the logic is the same. - mFakeTimeZoneDetectorStrategy.verifySuggestManualTimeZoneCalled( - mTestCallerIdentityInjector.getCallingUserId(), timeZoneSuggestion); + int expectedUserId = mTestCallerIdentityInjector.getCallingUserId(); + boolean expectedBypassUserPolicyChecks = false; + verify(mFakeTimeZoneDetectorStrategySpy).suggestManualTimeZone( + expectedUserId, timeZoneSuggestion, expectedBypassUserPolicyChecks); verify(mMockContext).enforceCallingPermission( eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); @@ -427,7 +441,7 @@ public class TimeZoneDetectorServiceTest { mTimeZoneDetectorService.dump(null, pw, null); verify(mMockContext).checkCallingOrSelfPermission(eq(android.Manifest.permission.DUMP)); - mFakeTimeZoneDetectorStrategy.verifyDumpCalled(); + verify(mFakeTimeZoneDetectorStrategySpy).dump(any(), any()); verify(dumpable).dump(any(), any()); } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java index 77d8b8bf9687..d0a7c92b0436 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java @@ -53,6 +53,7 @@ import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.Qualifie import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; import java.io.PrintWriter; import java.util.Arrays; @@ -60,9 +61,13 @@ import java.util.Collections; import java.util.List; import java.util.function.Function; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + /** * White-box unit tests for {@link TimeZoneDetectorStrategyImpl}. */ +@RunWith(JUnitParamsRunner.class) public class TimeZoneDetectorStrategyImplTest { private static final @UserIdInt int USER_ID = 9876; @@ -561,74 +566,79 @@ public class TimeZoneDetectorStrategyImplTest { .resetConfigurationTracking(); // Auto time zone detection is enabled so the manual suggestion should be ignored. - script.simulateManualTimeZoneSuggestion( - USER_ID, createManualSuggestion("Europe/Paris"), false /* expectedResult */) + boolean bypassUserPolicyChecks = false; + boolean expectedResult = false; + script.simulateManualTimeZoneSuggestion(USER_ID, createManualSuggestion("Europe/Paris"), + bypassUserPolicyChecks, expectedResult) .verifyTimeZoneNotChanged(); assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion()); } @Test - public void testManualSuggestion_restricted_simulateAutoTimeZoneEnabled() { - Script script = new Script() - .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW) - .simulateConfigurationInternalChange(CONFIG_USER_RESTRICTED_AUTO_ENABLED) - .resetConfigurationTracking(); - - // User is restricted so the manual suggestion should be ignored. - script.simulateManualTimeZoneSuggestion( - USER_ID, createManualSuggestion("Europe/Paris"), false /* expectedResult */) - .verifyTimeZoneNotChanged(); - - assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion()); - } - - @Test - public void testManualSuggestion_unrestricted_autoTimeZoneDetectionDisabled() { + public void testManualSuggestion_autoDetectNotSupported() { Script script = new Script() .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW) - .simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED_GEO_DISABLED) + .simulateConfigurationInternalChange(CONFIG_AUTO_DETECT_NOT_SUPPORTED) .resetConfigurationTracking(); - // Auto time zone detection is disabled so the manual suggestion should be used. + // Unrestricted users have the capability. ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris"); + boolean bypassUserPolicyChecks = false; + boolean expectedResult = true; script.simulateManualTimeZoneSuggestion( - USER_ID, manualSuggestion, true /* expectedResult */) - .verifyTimeZoneChangedAndReset(manualSuggestion); + USER_ID, manualSuggestion, bypassUserPolicyChecks, expectedResult) + .verifyTimeZoneChangedAndReset(manualSuggestion); assertEquals(manualSuggestion, mTimeZoneDetectorStrategy.getLatestManualSuggestion()); } @Test - public void testManualSuggestion_restricted_autoTimeZoneDetectionDisabled() { + @Parameters({ "true,true", "true,false", "false,true", "false,false" }) + public void testManualSuggestion_autoTimeEnabled_userRestrictions( + boolean userConfigAllowed, boolean bypassUserPolicyChecks) { + ConfigurationInternal config = + new ConfigurationInternal.Builder(CONFIG_USER_RESTRICTED_AUTO_ENABLED) + .setUserConfigAllowed(userConfigAllowed) + .build(); Script script = new Script() .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW) - .simulateConfigurationInternalChange(CONFIG_USER_RESTRICTED_AUTO_DISABLED) + .simulateConfigurationInternalChange(config) .resetConfigurationTracking(); - // Restricted users do not have the capability. - ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris"); - script.simulateManualTimeZoneSuggestion( - USER_ID, manualSuggestion, false /* expectedResult */) + // User is restricted so the manual suggestion should be ignored. + boolean expectedResult = false; + script.simulateManualTimeZoneSuggestion(USER_ID, createManualSuggestion("Europe/Paris"), + bypassUserPolicyChecks, expectedResult) .verifyTimeZoneNotChanged(); assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion()); } @Test - public void testManualSuggestion_autoDetectNotSupported() { + @Parameters({ "true,true", "true,false", "false,true", "false,false" }) + public void testManualSuggestion_autoTimeDisabled_userRestrictions( + boolean userConfigAllowed, boolean bypassUserPolicyChecks) { + ConfigurationInternal config = + new ConfigurationInternal.Builder(CONFIG_AUTO_DISABLED_GEO_DISABLED) + .setUserConfigAllowed(userConfigAllowed) + .build(); Script script = new Script() .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW) - .simulateConfigurationInternalChange(CONFIG_AUTO_DETECT_NOT_SUPPORTED) + .simulateConfigurationInternalChange(config) .resetConfigurationTracking(); - // Unrestricted users have the capability. ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris"); + boolean expectedResult = userConfigAllowed || bypassUserPolicyChecks; script.simulateManualTimeZoneSuggestion( - USER_ID, manualSuggestion, true /* expectedResult */) - .verifyTimeZoneChangedAndReset(manualSuggestion); - - assertEquals(manualSuggestion, mTimeZoneDetectorStrategy.getLatestManualSuggestion()); + USER_ID, manualSuggestion, bypassUserPolicyChecks, expectedResult); + if (expectedResult) { + script.verifyTimeZoneChangedAndReset(manualSuggestion); + assertEquals(manualSuggestion, mTimeZoneDetectorStrategy.getLatestManualSuggestion()); + } else { + script.verifyTimeZoneNotChanged(); + assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion()); + } } @Test @@ -1071,8 +1081,10 @@ public class TimeZoneDetectorStrategyImplTest { // Make sure the manual suggestion is recorded. ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Zone1"); - script.simulateManualTimeZoneSuggestion(USER_ID, manualSuggestion, - true /* expectedResult */) + boolean bypassUserPolicyChecks = false; + boolean expectedResult = true; + script.simulateManualTimeZoneSuggestion( + USER_ID, manualSuggestion, bypassUserPolicyChecks, expectedResult) .verifyTimeZoneChangedAndReset(manualSuggestion); expectedDeviceTimeZoneId = manualSuggestion.getZoneId(); assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId, @@ -1350,9 +1362,9 @@ public class TimeZoneDetectorStrategyImplTest { /** Simulates the time zone detection strategy receiving a user-originated suggestion. */ Script simulateManualTimeZoneSuggestion( @UserIdInt int userId, ManualTimeZoneSuggestion manualTimeZoneSuggestion, - boolean expectedResult) { + boolean bypassUserPolicyChecks, boolean expectedResult) { boolean actualResult = mTimeZoneDetectorStrategy.suggestManualTimeZone( - userId, manualTimeZoneSuggestion); + userId, manualTimeZoneSuggestion, bypassUserPolicyChecks); assertEquals(expectedResult, actualResult); return this; } |