From 15b701b09c87c09b2afb065b0c27537079453ca1 Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Tue, 4 Oct 2022 11:53:54 +0100 Subject: [PATCH] Add internal service paths for enterprise setup Add internal service paths for enterprise device initialization through the addition of several TimeDetectorInternal and TimeZoneDetectorInternal methods that are intended for use by DevicePolicyManagerService (in a later commit). Many of the changes to service code relate to the need to bypass user policy checks for the new usecases (which don't need to enforce them, because they are acting on behalf of a "device admin"). A check to enforce user policy checks for setting time has been added as part of this commit. The absence looks like an omission. This commit changes some permission checks in *Service classes to be more restrictive, i.e. enforceCallingOrSelfPermission() -> enforceCallingPermission(). Looking at the history the "OrSelf" was originally added for enterprise use cases (since calls were made via *Service), and some were just copy-and-pastes. With dedicated paths being added for enterprise via *Internal APIs, the "OrSelf" shouldn't be needed as there are no binder calls involved for enterprise. This commit switches some test code to using Mockito's spy() for call-tracking to simplify hand-rolled fakes. It also adds a new dependency on the junit-params for easier parameterization of test methods. Bug: 236612872 Test: atest services/tests/servicestests/src/com/android/server/timedetector/ Test: atest services/tests/servicestests/src/com/android/server/timezonedetector/ Change-Id: Ie5a8cccbb092077c000abd24854b70917d5e324d --- .../timedetector/ConfigurationInternal.java | 34 ++- .../timedetector/ServiceConfigAccessor.java | 10 +- .../ServiceConfigAccessorImpl.java | 6 +- .../timedetector/TimeDetectorInternal.java | 35 ++- .../TimeDetectorInternalImpl.java | 39 ++++ .../timedetector/TimeDetectorService.java | 29 ++- .../timedetector/TimeDetectorStrategy.java | 6 +- .../TimeDetectorStrategyImpl.java | 25 +- .../ConfigurationInternal.java | 25 +- .../CurrentUserIdentityInjector.java | 46 ++++ .../ServiceConfigAccessor.java | 12 +- .../ServiceConfigAccessorImpl.java | 6 +- .../TimeZoneDetectorInternal.java | 35 ++- .../TimeZoneDetectorInternalImpl.java | 39 ++++ .../TimeZoneDetectorService.java | 26 ++- .../TimeZoneDetectorStrategy.java | 6 +- .../TimeZoneDetectorStrategyImpl.java | 9 +- .../ConfigurationInternalTest.java | 221 +++++++++--------- .../FakeServiceConfigAccessor.java | 7 +- .../FakeTimeDetectorStrategy.java | 67 +----- .../TimeDetectorInternalImplTest.java | 109 ++++++++- .../timedetector/TimeDetectorServiceTest.java | 86 ++++--- .../TimeDetectorStrategyImplTest.java | 78 +++++-- .../ConfigurationInternalTest.java | 207 ++++++++-------- .../FakeServiceConfigAccessor.java | 7 +- .../FakeTimeZoneDetectorStrategy.java | 57 +---- .../TestCurrentUserIdentityInjector.java | 34 +++ .../TimeZoneDetectorInternalImplTest.java | 96 +++++++- .../TimeZoneDetectorServiceTest.java | 70 +++--- .../TimeZoneDetectorStrategyImplTest.java | 120 +++++----- 30 files changed, 995 insertions(+), 552 deletions(-) create mode 100644 services/core/java/com/android/server/timezonedetector/CurrentUserIdentityInjector.java create mode 100644 services/tests/servicestests/src/com/android/server/timezonedetector/TestCurrentUserIdentityInjector.java diff --git a/services/core/java/com/android/server/timedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java index d9a4266e28127..4f221b532b757 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). + * + *

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 25a74ceeb56d4..a39f64c1f82ac 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. * - *

This method returns {@code true} if the configuration was changed, - * {@code false} otherwise. + *

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 84013a7550354..71acf35bd3a21 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 eae12c28d2b95..24533d79fb18a 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. * *

The methods on this class can be called from any thread. + * + *

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. + * + *

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 5a3e20ebd6cdb..9839de0806909 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,15 +36,49 @@ 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 39672b847f303..64adbb680653e 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 bc86ed057fb62..03f236d9b30d2 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 c3f05cc22495c..3cee19cbe385a 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 d413febdcdb42..8e2a5f4725586 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). + * + *

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 0000000000000..f96b76dca9297 --- /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 692b0cc795f56..8da5d6aefdd66 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. * - *

This method returns {@code true} if the configuration was changed, - * {@code false} otherwise. + *

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 7173f60081775..e2f42467c5501 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 b6ce8026a2dc6..80cf1d6b9031b 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java @@ -17,16 +17,47 @@ 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. * *

The methods on this class can be called from any thread. + * + *

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. + * + *

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 diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java index f61df820c3e06..ce64eaccd98f7 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,15 +34,51 @@ 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) { diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index 822cd41243802..13f169461511f 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 e4b2df1460aec..69284e3226630 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 1e88c47e7b306..18c8885ee0593 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 a24afe6bc25e9..808c1ec2603b2 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,116 +56,86 @@ 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) - .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(); - { - 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(); - { - 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_NOT_ALLOWED, - capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_NOT_ALLOWED, 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_NOT_ALLOWED, - capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSetManualTimeCapability()); - - TimeConfiguration configuration = capabilitiesAndConfig.getConfiguration(); - assertFalse(configuration.isAutoDetectionEnabled()); - } - } - - /** Tests when {@link ConfigurationInternal#isAutoDetectionSupported()} is false. */ - @Test - public void test_autoDetectNotSupported() { + @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) + .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) + .build(); + assertTrue(autoOnConfig.getAutoDetectionEnabledSetting()); + assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior()); + + TimeCapabilitiesAndConfig capabilitiesAndConfig = + autoOnConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); + + TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); + 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()); + } + + { + ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig) + .setAutoDetectionEnabledSetting(false) + .build(); + assertFalse(autoOffConfig.getAutoDetectionEnabledSetting()); + assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior()); + + TimeCapabilitiesAndConfig capabilitiesAndConfig = + autoOffConfig.createCapabilitiesAndConfig(bypassUserPolicyChecks); + + TimeCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); + 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 {@link TimeCapabilitiesAndConfig} behavior in different scenarios when auto detection + * is not supported. + */ + @Test + @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(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 77b319b56acb6..a98a43b75d9bb 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 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 7b38fa0145120..856df359b3269 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 5b61752175681..a0845a64757e9 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 org.mockito.Mockito.mock; +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 @@ -62,6 +85,56 @@ public class TimeDetectorInternalImplTest { mHandlerThread.join(); } + @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 f776d3da0e791..4b417ba367ae6 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 08632284cfc89..62dae481ac0b7 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 810bd82f44312..7140097bb6c01 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,6 +68,10 @@ public class ConfigurationInternalTest { .setLocationEnabledSetting(true) .setGeoDetectionEnabledSetting(true) .build(); + + boolean userRestrictionsExpected = !(userConfigAllowed || bypassUserPolicyChecks); + + // Auto-detection enabled. { ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig) .setAutoDetectionEnabledSetting(true) @@ -70,13 +83,21 @@ public class ConfigurationInternalTest { assertEquals(DETECTION_MODE_GEO, 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()); + } + // This has user privacy implications so it is not restricted in the same way as others. assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabledCapability()); @@ -85,6 +106,7 @@ public class ConfigurationInternalTest { assertTrue(configuration.isGeoDetectionEnabled()); } + // Auto-detection disabled. { ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig) .setAutoDetectionEnabledSetting(false) @@ -96,81 +118,20 @@ 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()); - 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(); - { - 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_NOT_ALLOWED, - capabilities.getConfigureAutoDetectionEnabledCapability()); - assertEquals(CAPABILITY_NOT_ALLOWED, - capabilities.getSetManualTimeZoneCapability()); - // This has user privacy implications so it is not restricted in the same way as others. - assertEquals(CAPABILITY_POSSESSED, - capabilities.getConfigureGeoDetectionEnabledCapability()); - - TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration(); - assertTrue(configuration.isAutoDetectionEnabled()); - assertTrue(configuration.isGeoDetectionEnabled()); - } - - { - 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_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 a97ad8c57a0db..fdee86ea12857 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 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 339e5b23cd9a6..228dc952356a9 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 0000000000000..aad06d87f1113 --- /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 c5bab760fcfe4..276fdb9711721 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 ARBITRARY_ZONE_IDS = Arrays.asList("TestZoneId"); + private static final String ARBITRARY_ZONE_ID = "TestZoneId"; + private static final List 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 @@ -66,6 +79,56 @@ public class TimeZoneDetectorInternalImplTest { mHandlerThread.join(); } + @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(); @@ -73,11 +136,28 @@ public class TimeZoneDetectorInternalImplTest { 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 fc6459725978d..bb9d564b5aad8 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 77d8b8bf96879..d0a7c92b04364 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,55 +566,10 @@ 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 */) - .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() { - Script script = new Script() - .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW) - .simulateConfigurationInternalChange(CONFIG_AUTO_DISABLED_GEO_DISABLED) - .resetConfigurationTracking(); - - // Auto time zone detection is disabled so the manual suggestion should be used. - ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris"); - script.simulateManualTimeZoneSuggestion( - USER_ID, manualSuggestion, true /* expectedResult */) - .verifyTimeZoneChangedAndReset(manualSuggestion); - - assertEquals(manualSuggestion, mTimeZoneDetectorStrategy.getLatestManualSuggestion()); - } - - @Test - public void testManualSuggestion_restricted_autoTimeZoneDetectionDisabled() { - Script script = new Script() - .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID, TIME_ZONE_CONFIDENCE_LOW) - .simulateConfigurationInternalChange(CONFIG_USER_RESTRICTED_AUTO_DISABLED) - .resetConfigurationTracking(); - - // Restricted users do not have the capability. - ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris"); - script.simulateManualTimeZoneSuggestion( - USER_ID, manualSuggestion, false /* expectedResult */) + boolean bypassUserPolicyChecks = false; + boolean expectedResult = false; + script.simulateManualTimeZoneSuggestion(USER_ID, createManualSuggestion("Europe/Paris"), + bypassUserPolicyChecks, expectedResult) .verifyTimeZoneNotChanged(); assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion()); @@ -624,13 +584,63 @@ public class TimeZoneDetectorStrategyImplTest { // Unrestricted users have the capability. ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris"); + boolean bypassUserPolicyChecks = false; + boolean expectedResult = true; script.simulateManualTimeZoneSuggestion( - USER_ID, manualSuggestion, true /* expectedResult */) + USER_ID, manualSuggestion, bypassUserPolicyChecks, expectedResult) .verifyTimeZoneChangedAndReset(manualSuggestion); assertEquals(manualSuggestion, mTimeZoneDetectorStrategy.getLatestManualSuggestion()); } + @Test + @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) + .resetConfigurationTracking(); + + // 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 + @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) + .resetConfigurationTracking(); + + ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris"); + boolean expectedResult = userConfigAllowed || bypassUserPolicyChecks; + script.simulateManualTimeZoneSuggestion( + USER_ID, manualSuggestion, bypassUserPolicyChecks, expectedResult); + if (expectedResult) { + script.verifyTimeZoneChangedAndReset(manualSuggestion); + assertEquals(manualSuggestion, mTimeZoneDetectorStrategy.getLatestManualSuggestion()); + } else { + script.verifyTimeZoneNotChanged(); + assertNull(mTimeZoneDetectorStrategy.getLatestManualSuggestion()); + } + } + @Test public void testGeoSuggestion_uncertain() { Script script = new Script() @@ -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; }