diff options
| author | 2020-08-27 17:47:38 +0100 | |
|---|---|---|
| committer | 2020-09-30 13:34:05 +0100 | |
| commit | 02d943f44d4ed11a7cde1fa33a4c0c07b9cb7e3c (patch) | |
| tree | 11ecadb8d4213809b3d3c1bbdb0bdd4c3c5bd151 | |
| parent | 78733751fe1a549ea938d0703a81f4b3442ae74b (diff) | |
Get ready to add System APIs for use by SUW
This commit introduces the TimeManager API class. It is intended to
provide access to the time and time zone detector services, which
historically have had their own (internal) API classes to access them.
This commit contains various fixes to the listener handling logic
noticed while testing the APIs.
The first APIs on TimeManager are being exposed for use by SetUp Wizard,
which requires System APIs.
The APIs enable the calling app to determine the capabilities for /
config of the current user that affect time zone detection and then
alter them.
The primary usecase:
The rules for who can do what and when for time zone detection are
becoming more complicated, and device and user capabilities will be
affected by compile time constants, device config (resources) and device
hardware. By exposing this API, the complex logic can be encoded in one
place (the system server), rather than duplicating them in multiple
client apps.
These APIs are also intended for use by SettingsUI (and the various form
factor versions of that), but settings apps do not currently require
them to be marked with @SystemApi.
Bug: 159891384
Test: atest services/tests/servicestests/src/com/android/server/timezonedetector/
Test: atest android.app.time
Change-Id: I9b59099748e2391c180e124edf83fa521beb1b51
26 files changed, 988 insertions, 717 deletions
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index d50cdeed6d73..9100d577fd68 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -33,6 +33,7 @@ import android.app.prediction.AppPredictionManager; import android.app.role.RoleControllerManager; import android.app.role.RoleManager; import android.app.slice.SliceManager; +import android.app.time.TimeManager; import android.app.timedetector.TimeDetector; import android.app.timedetector.TimeDetectorImpl; import android.app.timezone.RulesManager; @@ -1218,6 +1219,14 @@ public final class SystemServiceRegistry { return new TimeZoneDetectorImpl(); }}); + registerService(Context.TIME_MANAGER, TimeManager.class, + new CachedServiceFetcher<TimeManager>() { + @Override + public TimeManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + return new TimeManager(); + }}); + registerService(Context.PERMISSION_SERVICE, PermissionManager.class, new CachedServiceFetcher<PermissionManager>() { @Override diff --git a/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl b/core/java/android/app/time/ITimeZoneDetectorListener.aidl index 6d0fe72b9de1..723ad5969afc 100644 --- a/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl +++ b/core/java/android/app/time/ITimeZoneDetectorListener.aidl @@ -14,11 +14,9 @@ * limitations under the License. */ -package android.app.timezonedetector; - -import android.app.timezonedetector.TimeZoneConfiguration; +package android.app.time; /** {@hide} */ -oneway interface ITimeZoneConfigurationListener { +oneway interface ITimeZoneDetectorListener { void onChange(); }
\ No newline at end of file diff --git a/core/java/android/app/time/TEST_MAPPING b/core/java/android/app/time/TEST_MAPPING new file mode 100644 index 000000000000..951905bcbac5 --- /dev/null +++ b/core/java/android/app/time/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.app.time." + } + ] + } + ] +} diff --git a/core/java/android/app/time/TimeManager.java b/core/java/android/app/time/TimeManager.java new file mode 100644 index 000000000000..9864afba534a --- /dev/null +++ b/core/java/android/app/time/TimeManager.java @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemService; +import android.app.timezonedetector.ITimeZoneDetectorService; +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceManager.ServiceNotFoundException; +import android.util.ArrayMap; +import android.util.Log; + +import com.android.internal.annotations.GuardedBy; + +import java.util.concurrent.Executor; + +/** + * The interface through which system components can interact with time and time zone services. + * + * @hide + */ +// @SystemApi +@SystemService(Context.TIME_MANAGER) +public final class TimeManager { + private static final String TAG = "time.TimeManager"; + private static final boolean DEBUG = false; + + private final Object mLock = new Object(); + private final ITimeZoneDetectorService mITimeZoneDetectorService; + + @GuardedBy("mLock") + private ITimeZoneDetectorListener mTimeZoneDetectorReceiver; + + /** + * The registered listeners. The key is the actual listener that was registered, the value is a + * wrapper that ensures the listener is executed on the correct Executor. + */ + @GuardedBy("mLock") + private ArrayMap<TimeZoneDetectorListener, TimeZoneDetectorListener> mTimeZoneDetectorListeners; + + /** @hide */ + public TimeManager() throws ServiceNotFoundException { + // TimeManager is an API over one or possibly more services. At least until there's an + // internal refactoring. + mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface( + ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE)); + } + + /** + * Returns the calling user's time zone capabilities and configuration. + */ + @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) + @NonNull + public TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig() { + if (DEBUG) { + Log.d(TAG, "getTimeZoneCapabilities called"); + } + try { + return mITimeZoneDetectorService.getCapabilitiesAndConfig(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Modifies the time zone detection configuration. + * + * <p>Configuration settings vary in scope: some may be global (affect all users), others may be + * specific to the current user. + * + * <p>The ability to modify configuration settings can be subject to restrictions. For + * example, they may be determined by device hardware, general policy (i.e. only the primary + * user can set them), or by a managed device policy. Use {@link + * #getTimeZoneCapabilitiesAndConfig()} to obtain information at runtime about the user's + * capabilities. + * + * <p>Attempts to modify configuration settings with capabilities that are {@link + * TimeZoneCapabilities#CAPABILITY_NOT_SUPPORTED} or {@link + * TimeZoneCapabilities#CAPABILITY_NOT_ALLOWED} will have no effect and a {@code false} + * will be returned. Modifying configuration settings with capabilities that are {@link + * TimeZoneCapabilities#CAPABILITY_NOT_APPLICABLE} or {@link + * TimeZoneCapabilities#CAPABILITY_POSSESSED} will succeed. See {@link + * TimeZoneCapabilities} for further details. + * + * <p>If the supplied configuration only has some values set, then only the specified settings + * will be updated (where the user's capabilities allow) and other settings will be left + * unchanged. + * + * @return {@code true} if all the configuration settings specified have been set to the + * new values, {@code false} if none have + */ + @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) + public boolean updateTimeZoneConfiguration(@NonNull TimeZoneConfiguration configuration) { + if (DEBUG) { + Log.d(TAG, "updateConfiguration called: " + configuration); + } + try { + return mITimeZoneDetectorService.updateConfiguration(configuration); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * An interface that can be used to listen for changes to the time zone detector behavior. + */ + @FunctionalInterface + public interface TimeZoneDetectorListener { + /** + * Called when something about the time zone detector behavior on the device has changed. + * For example, this could be because the current user has switched, one of the global or + * user's settings been changed, or something that could affect a user's capabilities with + * respect to the time zone detector has changed. Because different users can have different + * configuration and capabilities, this method may be called when nothing has changed for + * the receiving user. + */ + void onChange(); + } + + /** + * Registers a listener that will be informed when something about the time zone detector + * behavior changes. + */ + @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) + public void addTimeZoneDetectorListener(@NonNull Executor executor, + @NonNull TimeZoneDetectorListener listener) { + + if (DEBUG) { + Log.d(TAG, "addTimeZoneDetectorListener called: " + listener); + } + synchronized (mLock) { + if (mTimeZoneDetectorListeners == null) { + mTimeZoneDetectorListeners = new ArrayMap<>(); + } else if (mTimeZoneDetectorListeners.containsKey(listener)) { + return; + } + + if (mTimeZoneDetectorReceiver == null) { + ITimeZoneDetectorListener iListener = new ITimeZoneDetectorListener.Stub() { + @Override + public void onChange() { + notifyTimeZoneDetectorListeners(); + } + }; + mTimeZoneDetectorReceiver = iListener; + try { + mITimeZoneDetectorService.addListener(mTimeZoneDetectorReceiver); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + mTimeZoneDetectorListeners.put(listener, () -> executor.execute(listener::onChange)); + } + } + + private void notifyTimeZoneDetectorListeners() { + ArrayMap<TimeZoneDetectorListener, TimeZoneDetectorListener> timeZoneDetectorListeners; + synchronized (mLock) { + if (mTimeZoneDetectorListeners == null || mTimeZoneDetectorListeners.isEmpty()) { + return; + } + timeZoneDetectorListeners = new ArrayMap<>(mTimeZoneDetectorListeners); + } + int size = timeZoneDetectorListeners.size(); + for (int i = 0; i < size; i++) { + timeZoneDetectorListeners.valueAt(i).onChange(); + } + } + + /** + * Removes a listener previously passed to + * {@link #addTimeZoneDetectorListener(Executor, TimeZoneDetectorListener)} + */ + @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) + public void removeTimeZoneDetectorListener(@NonNull TimeZoneDetectorListener listener) { + if (DEBUG) { + Log.d(TAG, "removeConfigurationListener called: " + listener); + } + + synchronized (mLock) { + if (mTimeZoneDetectorListeners == null || mTimeZoneDetectorListeners.isEmpty()) { + return; + } + mTimeZoneDetectorListeners.remove(listener); + + // If the last local listener has been removed, remove and discard the + // mTimeZoneDetectorReceiver. + if (mTimeZoneDetectorListeners.isEmpty()) { + try { + mITimeZoneDetectorService.removeListener(mTimeZoneDetectorReceiver); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } finally { + mTimeZoneDetectorReceiver = null; + } + } + } + } +} diff --git a/core/java/android/app/timezonedetector/TimeZoneCapabilities.aidl b/core/java/android/app/time/TimeZoneCapabilities.aidl index fede6458318a..f744bf162c67 100644 --- a/core/java/android/app/timezonedetector/TimeZoneCapabilities.aidl +++ b/core/java/android/app/time/TimeZoneCapabilities.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.app.timezonedetector; +package android.app.time; parcelable TimeZoneCapabilities; diff --git a/core/java/android/app/timezonedetector/TimeZoneCapabilities.java b/core/java/android/app/time/TimeZoneCapabilities.java index 09fffe9f4f25..c62c2b34f35d 100644 --- a/core/java/android/app/timezonedetector/TimeZoneCapabilities.java +++ b/core/java/android/app/time/TimeZoneCapabilities.java @@ -14,16 +14,16 @@ * limitations under the License. */ -package android.app.timezonedetector; - -import static android.app.timezonedetector.TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED; -import static android.app.timezonedetector.TimeZoneConfiguration.SETTING_GEO_DETECTION_ENABLED; +package android.app.time; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.timezonedetector.ManualTimeZoneSuggestion; +import android.app.timezonedetector.TimeZoneDetector; import android.os.Parcel; import android.os.Parcelable; +import android.os.UserHandle; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -42,15 +42,18 @@ import java.util.Objects; * <p>Actions have associated methods, see the documentation for each action for details. * * <p>For configuration settings capabilities, the associated settings value can be found via - * {@link #getConfiguration()} and may be changed using {@link - * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} (if the user's capabilities allow). + * {@link TimeManager#getTimeZoneCapabilitiesAndConfig()} and may be changed using {@link + * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)} (if the user's capabilities + * allow). * * <p>Note: Capabilities are independent of app permissions required to call the associated APIs. * * @hide */ +// @SystemApi public final class TimeZoneCapabilities implements Parcelable { + /** @hide */ @IntDef({ CAPABILITY_NOT_SUPPORTED, CAPABILITY_NOT_ALLOWED, CAPABILITY_NOT_APPLICABLE, CAPABILITY_POSSESSED }) @Retention(RetentionPolicy.SOURCE) @@ -94,64 +97,60 @@ public final class TimeZoneCapabilities implements Parcelable { } }; - - @NonNull private final TimeZoneConfiguration mConfiguration; - private final @CapabilityState int mConfigureAutoDetectionEnabled; - private final @CapabilityState int mConfigureGeoDetectionEnabled; - private final @CapabilityState int mSuggestManualTimeZone; + /** + * The user the capabilities are for. This is used for object equality and debugging but there + * is no accessor. + */ + @NonNull private final UserHandle mUserHandle; + private final @CapabilityState int mConfigureAutoDetectionEnabledCapability; + private final @CapabilityState int mConfigureGeoDetectionEnabledCapability; + private final @CapabilityState int mSuggestManualTimeZoneCapability; private TimeZoneCapabilities(@NonNull Builder builder) { - this.mConfiguration = Objects.requireNonNull(builder.mConfiguration); - this.mConfigureAutoDetectionEnabled = builder.mConfigureAutoDetectionEnabled; - this.mConfigureGeoDetectionEnabled = builder.mConfigureGeoDetectionEnabled; - this.mSuggestManualTimeZone = builder.mSuggestManualTimeZone; + this.mUserHandle = Objects.requireNonNull(builder.mUserHandle); + this.mConfigureAutoDetectionEnabledCapability = + builder.mConfigureAutoDetectionEnabledCapability; + this.mConfigureGeoDetectionEnabledCapability = + builder.mConfigureGeoDetectionEnabledCapability; + this.mSuggestManualTimeZoneCapability = builder.mSuggestManualTimeZoneCapability; } @NonNull private static TimeZoneCapabilities createFromParcel(Parcel in) { - return new TimeZoneCapabilities.Builder() - .setConfiguration(in.readParcelable(null)) - .setConfigureAutoDetectionEnabled(in.readInt()) - .setConfigureGeoDetectionEnabled(in.readInt()) - .setSuggestManualTimeZone(in.readInt()) + UserHandle userHandle = UserHandle.readFromParcel(in); + return new TimeZoneCapabilities.Builder(userHandle) + .setConfigureAutoDetectionEnabledCapability(in.readInt()) + .setConfigureGeoDetectionEnabledCapability(in.readInt()) + .setSuggestManualTimeZoneCapability(in.readInt()) .build(); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(mConfiguration, flags); - dest.writeInt(mConfigureAutoDetectionEnabled); - dest.writeInt(mConfigureGeoDetectionEnabled); - dest.writeInt(mSuggestManualTimeZone); - } - - /** - * Returns the user's time zone behavior configuration. - */ - public @NonNull TimeZoneConfiguration getConfiguration() { - return mConfiguration; + UserHandle.writeToParcel(mUserHandle, dest); + dest.writeInt(mConfigureAutoDetectionEnabledCapability); + dest.writeInt(mConfigureGeoDetectionEnabledCapability); + dest.writeInt(mSuggestManualTimeZoneCapability); } /** * Returns the capability state associated with the user's ability to modify the automatic time * zone detection setting. The setting can be updated via {@link - * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and accessed via {@link - * #getConfiguration()}. + * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)}. */ @CapabilityState - public int getConfigureAutoDetectionEnabled() { - return mConfigureAutoDetectionEnabled; + public int getConfigureAutoDetectionEnabledCapability() { + return mConfigureAutoDetectionEnabledCapability; } /** * Returns the capability state associated with the user's ability to modify the geolocation * detection setting. The setting can be updated via {@link - * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and accessed via {@link - * #getConfiguration()}. + * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)}. */ @CapabilityState - public int getConfigureGeoDetectionEnabled() { - return mConfigureGeoDetectionEnabled; + public int getConfigureGeoDetectionEnabledCapability() { + return mConfigureGeoDetectionEnabledCapability; } /** @@ -160,36 +159,37 @@ public final class TimeZoneCapabilities implements Parcelable { * * <p>The suggestion will be ignored in all cases unless the value is {@link * #CAPABILITY_POSSESSED}. See also {@link TimeZoneConfiguration#isAutoDetectionEnabled()}. + * + * @hide */ @CapabilityState - public int getSuggestManualTimeZone() { - return mSuggestManualTimeZone; + public int getSuggestManualTimeZoneCapability() { + return mSuggestManualTimeZoneCapability; } /** - * Constructs a new {@link TimeZoneConfiguration} from an {@code oldConfiguration} and a set of - * {@code requestedChanges}, if the current capabilities allow. The new configuration is - * returned and the capabilities are left unchanged. If the capabilities do not permit one or - * more of the changes then {@code null} is returned. + * Tries to create a new {@link TimeZoneConfiguration} from the {@code config} and the set of + * {@code requestedChanges}, if {@code this} capabilities allow. The new configuration is + * returned. If the capabilities do not permit one or more of the requested changes then {@code + * null} is returned. + * + * @hide */ @Nullable - public TimeZoneConfiguration applyUpdate(TimeZoneConfiguration requestedChanges) { - if (requestedChanges.getUserId() != mConfiguration.getUserId()) { - throw new IllegalArgumentException("User does not match:" - + " this=" + mConfiguration + ", other=" + requestedChanges); - } - + public TimeZoneConfiguration tryApplyConfigChanges( + @NonNull TimeZoneConfiguration config, + @NonNull TimeZoneConfiguration requestedChanges) { TimeZoneConfiguration.Builder newConfigBuilder = - new TimeZoneConfiguration.Builder(mConfiguration); - if (requestedChanges.hasSetting(SETTING_AUTO_DETECTION_ENABLED)) { - if (getConfigureAutoDetectionEnabled() < CAPABILITY_NOT_APPLICABLE) { + new TimeZoneConfiguration.Builder(config); + if (requestedChanges.hasIsAutoDetectionEnabled()) { + if (this.getConfigureAutoDetectionEnabledCapability() < CAPABILITY_NOT_APPLICABLE) { return null; } newConfigBuilder.setAutoDetectionEnabled(requestedChanges.isAutoDetectionEnabled()); } - if (requestedChanges.hasSetting(SETTING_GEO_DETECTION_ENABLED)) { - if (getConfigureGeoDetectionEnabled() < CAPABILITY_NOT_APPLICABLE) { + if (requestedChanges.hasIsGeoDetectionEnabled()) { + if (this.getConfigureGeoDetectionEnabledCapability() < CAPABILITY_NOT_APPLICABLE) { return null; } newConfigBuilder.setGeoDetectionEnabled(requestedChanges.isGeoDetectionEnabled()); @@ -212,71 +212,71 @@ public final class TimeZoneCapabilities implements Parcelable { return false; } TimeZoneCapabilities that = (TimeZoneCapabilities) o; - return Objects.equals(mConfiguration, that.mConfiguration) - && mConfigureAutoDetectionEnabled == that.mConfigureAutoDetectionEnabled - && mConfigureGeoDetectionEnabled == that.mConfigureGeoDetectionEnabled - && mSuggestManualTimeZone == that.mSuggestManualTimeZone; + return mUserHandle.equals(that.mUserHandle) + && mConfigureAutoDetectionEnabledCapability + == that.mConfigureAutoDetectionEnabledCapability + && mConfigureGeoDetectionEnabledCapability + == that.mConfigureGeoDetectionEnabledCapability + && mSuggestManualTimeZoneCapability == that.mSuggestManualTimeZoneCapability; } @Override public int hashCode() { - return Objects.hash(mConfiguration, - mConfigureAutoDetectionEnabled, - mConfigureGeoDetectionEnabled, - mSuggestManualTimeZone); + return Objects.hash(mUserHandle, mConfigureAutoDetectionEnabledCapability, + mConfigureGeoDetectionEnabledCapability, mSuggestManualTimeZoneCapability); } @Override public String toString() { return "TimeZoneDetectorCapabilities{" - + "mConfiguration=" + mConfiguration - + ", mConfigureAutomaticDetectionEnabled=" + mConfigureAutoDetectionEnabled - + ", mConfigureGeoDetectionEnabled=" + mConfigureGeoDetectionEnabled - + ", mSuggestManualTimeZone=" + mSuggestManualTimeZone + + "mUserHandle=" + mUserHandle + + ", mConfigureAutoDetectionEnabledCapability=" + + mConfigureAutoDetectionEnabledCapability + + ", mConfigureGeoDetectionEnabledCapability=" + + mConfigureGeoDetectionEnabledCapability + + ", mSuggestManualTimeZoneCapability=" + mSuggestManualTimeZoneCapability + '}'; } /** @hide */ public static class Builder { - private TimeZoneConfiguration mConfiguration; - private @CapabilityState int mConfigureAutoDetectionEnabled; - private @CapabilityState int mConfigureGeoDetectionEnabled; - private @CapabilityState int mSuggestManualTimeZone; + @NonNull private UserHandle mUserHandle; + private @CapabilityState int mConfigureAutoDetectionEnabledCapability; + private @CapabilityState int mConfigureGeoDetectionEnabledCapability; + private @CapabilityState int mSuggestManualTimeZoneCapability; - /** Sets the user-visible configuration settings. */ - public Builder setConfiguration(@NonNull TimeZoneConfiguration configuration) { - if (!configuration.isComplete()) { - throw new IllegalArgumentException(configuration + " is not complete"); - } - this.mConfiguration = configuration; - return this; + public Builder(@NonNull UserHandle userHandle) { + mUserHandle = Objects.requireNonNull(userHandle); } /** Sets the state for the automatic time zone detection enabled config. */ - public Builder setConfigureAutoDetectionEnabled(@CapabilityState int value) { - this.mConfigureAutoDetectionEnabled = value; + public Builder setConfigureAutoDetectionEnabledCapability(@CapabilityState int value) { + this.mConfigureAutoDetectionEnabledCapability = value; return this; } /** Sets the state for the geolocation time zone detection enabled config. */ - public Builder setConfigureGeoDetectionEnabled(@CapabilityState int value) { - this.mConfigureGeoDetectionEnabled = value; + public Builder setConfigureGeoDetectionEnabledCapability(@CapabilityState int value) { + this.mConfigureGeoDetectionEnabledCapability = value; return this; } /** Sets the state for the suggestManualTimeZone action. */ - public Builder setSuggestManualTimeZone(@CapabilityState int value) { - this.mSuggestManualTimeZone = value; + public Builder setSuggestManualTimeZoneCapability(@CapabilityState int value) { + this.mSuggestManualTimeZoneCapability = value; return this; } /** Returns the {@link TimeZoneCapabilities}. */ @NonNull public TimeZoneCapabilities build() { - verifyCapabilitySet(mConfigureAutoDetectionEnabled, "configureAutoDetectionEnabled"); - verifyCapabilitySet(mConfigureGeoDetectionEnabled, "configureGeoDetectionEnabled"); - verifyCapabilitySet(mSuggestManualTimeZone, "suggestManualTimeZone"); + verifyCapabilitySet(mConfigureAutoDetectionEnabledCapability, + "configureAutoDetectionEnabledCapability"); + verifyCapabilitySet(mConfigureGeoDetectionEnabledCapability, + "configureGeoDetectionEnabledCapability"); + verifyCapabilitySet(mSuggestManualTimeZoneCapability, + "suggestManualTimeZoneCapability"); return new TimeZoneCapabilities(this); } diff --git a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.aidl b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.aidl new file mode 100644 index 000000000000..d7b6b58bf85a --- /dev/null +++ b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + +parcelable TimeZoneCapabilitiesAndConfig; diff --git a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java new file mode 100644 index 000000000000..6a04f3f277ed --- /dev/null +++ b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * A pair containing a user's {@link TimeZoneCapabilities} and {@link TimeZoneConfiguration}. + * + * @hide + */ +// @SystemApi +public final class TimeZoneCapabilitiesAndConfig implements Parcelable { + + public static final @NonNull Creator<TimeZoneCapabilitiesAndConfig> CREATOR = + new Creator<TimeZoneCapabilitiesAndConfig>() { + public TimeZoneCapabilitiesAndConfig createFromParcel(Parcel in) { + return TimeZoneCapabilitiesAndConfig.createFromParcel(in); + } + + public TimeZoneCapabilitiesAndConfig[] newArray(int size) { + return new TimeZoneCapabilitiesAndConfig[size]; + } + }; + + + @NonNull private final TimeZoneCapabilities mCapabilities; + @NonNull private final TimeZoneConfiguration mConfiguration; + + /** + * Creates a new instance. + * + * @hide + */ + public TimeZoneCapabilitiesAndConfig( + @NonNull TimeZoneCapabilities capabilities, + @NonNull TimeZoneConfiguration configuration) { + this.mCapabilities = Objects.requireNonNull(capabilities); + this.mConfiguration = Objects.requireNonNull(configuration); + } + + @NonNull + private static TimeZoneCapabilitiesAndConfig createFromParcel(Parcel in) { + TimeZoneCapabilities capabilities = in.readParcelable(null); + TimeZoneConfiguration configuration = in.readParcelable(null); + return new TimeZoneCapabilitiesAndConfig(capabilities, configuration); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelable(mCapabilities, flags); + dest.writeParcelable(mConfiguration, flags); + } + + /** + * Returns the user's time zone behavior capabilities. + */ + @NonNull + public TimeZoneCapabilities getCapabilities() { + return mCapabilities; + } + + /** + * Returns the user's time zone behavior configuration. + */ + @NonNull + public TimeZoneConfiguration getConfiguration() { + return mConfiguration; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TimeZoneCapabilitiesAndConfig that = (TimeZoneCapabilitiesAndConfig) o; + return mCapabilities.equals(that.mCapabilities) + && mConfiguration.equals(that.mConfiguration); + } + + @Override + public int hashCode() { + return Objects.hash(mCapabilities, mConfiguration); + } + + @Override + public String toString() { + return "TimeZoneDetectorCapabilitiesAndConfig{" + + "mCapabilities=" + mCapabilities + + ", mConfiguration=" + mConfiguration + + '}'; + } +} diff --git a/core/java/android/app/timezonedetector/TimeZoneConfiguration.aidl b/core/java/android/app/time/TimeZoneConfiguration.aidl index 62240ba5946b..8e859299d073 100644 --- a/core/java/android/app/timezonedetector/TimeZoneConfiguration.aidl +++ b/core/java/android/app/time/TimeZoneConfiguration.aidl @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.app.timezonedetector; +package android.app.time; parcelable TimeZoneConfiguration; diff --git a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java b/core/java/android/app/time/TimeZoneConfiguration.java index e879091cd68e..488818a528ef 100644 --- a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java +++ b/core/java/android/app/time/TimeZoneConfiguration.java @@ -14,11 +14,10 @@ * limitations under the License. */ -package android.app.timezonedetector; +package android.app.time; import android.annotation.NonNull; import android.annotation.StringDef; -import android.annotation.UserIdInt; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -36,15 +35,13 @@ import java.util.Objects; * several settings, the device behavior may not be directly affected by the setting value. * * <p>Settings can be left absent when updating configuration via {@link - * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and those settings will not be + * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)} and those settings will not be * changed. Not all configuration settings can be modified by all users: see {@link - * TimeZoneDetector#getCapabilities()} and {@link TimeZoneCapabilities} for details. - * - * <p>See {@link #hasSetting(String)} with {@code PROPERTY_} constants for testing for the presence - * of individual settings. + * TimeManager#getTimeZoneCapabilitiesAndConfig()} and {@link TimeZoneCapabilities} for details. * * @hide */ +// @SystemApi public final class TimeZoneConfiguration implements Parcelable { public static final @NonNull Creator<TimeZoneConfiguration> CREATOR = @@ -58,53 +55,48 @@ public final class TimeZoneConfiguration implements Parcelable { } }; - /** All configuration properties */ + /** + * All configuration properties + * + * @hide + */ @StringDef({ SETTING_AUTO_DETECTION_ENABLED, SETTING_GEO_DETECTION_ENABLED }) @Retention(RetentionPolicy.SOURCE) @interface Setting {} /** See {@link TimeZoneConfiguration#isAutoDetectionEnabled()} for details. */ @Setting - public static final String SETTING_AUTO_DETECTION_ENABLED = "autoDetectionEnabled"; + private static final String SETTING_AUTO_DETECTION_ENABLED = "autoDetectionEnabled"; /** See {@link TimeZoneConfiguration#isGeoDetectionEnabled()} for details. */ @Setting - public static final String SETTING_GEO_DETECTION_ENABLED = "geoDetectionEnabled"; + private static final String SETTING_GEO_DETECTION_ENABLED = "geoDetectionEnabled"; - private final @UserIdInt int mUserId; @NonNull private final Bundle mBundle; private TimeZoneConfiguration(Builder builder) { - this.mUserId = builder.mUserId; this.mBundle = Objects.requireNonNull(builder.mBundle); } private static TimeZoneConfiguration createFromParcel(Parcel in) { - return new TimeZoneConfiguration.Builder(in.readInt()) + return new TimeZoneConfiguration.Builder() .setPropertyBundleInternal(in.readBundle()) .build(); } @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mUserId); dest.writeBundle(mBundle); } - /** Returns the ID of the user this configuration is associated with. */ - public @UserIdInt int getUserId() { - return mUserId; - } - - /** Returns {@code true} if all known settings are present. */ + /** + * Returns {@code true} if all known settings are present. + * + * @hide + */ public boolean isComplete() { - return hasSetting(SETTING_AUTO_DETECTION_ENABLED) - && hasSetting(SETTING_GEO_DETECTION_ENABLED); - } - - /** Returns true if the specified setting is set. */ - public boolean hasSetting(@Setting String setting) { - return mBundle.containsKey(setting); + return hasIsAutoDetectionEnabled() + && hasIsGeoDetectionEnabled(); } /** @@ -112,9 +104,10 @@ public final class TimeZoneConfiguration implements Parcelable { * controls whether a device will attempt to determine the time zone automatically using * contextual information if the device supports auto detection. * - * <p>This setting is global and can be updated by some users. + * <p>See {@link TimeZoneCapabilities#getConfigureAutoDetectionEnabledCapability()} for how to + * tell if the setting is meaningful for the current user at this time. * - * @throws IllegalStateException if the setting has not been set + * @throws IllegalStateException if the setting is not present */ public boolean isAutoDetectionEnabled() { enforceSettingPresent(SETTING_AUTO_DETECTION_ENABLED); @@ -122,21 +115,39 @@ public final class TimeZoneConfiguration implements Parcelable { } /** + * Returns {@code true} if the {@link #isAutoDetectionEnabled()} setting is present. + * + * @hide + */ + public boolean hasIsAutoDetectionEnabled() { + return mBundle.containsKey(SETTING_AUTO_DETECTION_ENABLED); + } + + /** * Returns the value of the {@link #SETTING_GEO_DETECTION_ENABLED} setting. This - * controls whether a device can use geolocation to determine time zone. Only used when - * {@link #isAutoDetectionEnabled()} is {@code true} and when the user has allowed their - * location to be used. + * controls whether the device can use geolocation to determine time zone. This value may only + * be used by Android under some circumstances. For example, it is not used when + * {@link #isGeoDetectionEnabled()} is {@code false}. * - * <p>This setting is user-scoped and can be updated by some users. - * See {@link TimeZoneCapabilities#getConfigureGeoDetectionEnabled()}. + * <p>See {@link TimeZoneCapabilities#getConfigureGeoDetectionEnabledCapability()} for how to + * tell if the setting is meaningful for the current user at this time. * - * @throws IllegalStateException if the setting has not been set + * @throws IllegalStateException if the setting is not present */ public boolean isGeoDetectionEnabled() { enforceSettingPresent(SETTING_GEO_DETECTION_ENABLED); return mBundle.getBoolean(SETTING_GEO_DETECTION_ENABLED); } + /** + * Returns {@code true} if the {@link #isGeoDetectionEnabled()} setting is present. + * + * @hide + */ + public boolean hasIsGeoDetectionEnabled() { + return mBundle.containsKey(SETTING_GEO_DETECTION_ENABLED); + } + @Override public int describeContents() { return 0; @@ -151,20 +162,18 @@ public final class TimeZoneConfiguration implements Parcelable { return false; } TimeZoneConfiguration that = (TimeZoneConfiguration) o; - return mUserId == that.mUserId - && mBundle.kindofEquals(that.mBundle); + return mBundle.kindofEquals(that.mBundle); } @Override public int hashCode() { - return Objects.hash(mUserId, mBundle); + return Objects.hash(mBundle); } @Override public String toString() { return "TimeZoneConfiguration{" - + "mUserId=" + mUserId - + ", mBundle=" + mBundle + + "mBundle=" + mBundle + '}'; } @@ -174,43 +183,43 @@ public final class TimeZoneConfiguration implements Parcelable { } } - /** @hide */ - public static class Builder { + /** + * A builder for {@link TimeZoneConfiguration} objects. + * + * @hide + */ + // @SystemApi + public static final class Builder { - private final @UserIdInt int mUserId; private final Bundle mBundle = new Bundle(); /** - * Creates a new Builder for a userId with no settings held. + * Creates a new Builder with no settings held. */ - public Builder(@UserIdInt int userId) { - mUserId = userId; + public Builder() { } /** - * Creates a new Builder by copying the user ID and settings from an existing instance. + * Creates a new Builder by copying the settings from an existing instance. */ - public Builder(TimeZoneConfiguration toCopy) { - this.mUserId = toCopy.mUserId; + public Builder(@NonNull TimeZoneConfiguration toCopy) { mergeProperties(toCopy); } /** * Merges {@code other} settings into this instances, replacing existing values in this * where the settings appear in both. + * + * @hide */ - public Builder mergeProperties(TimeZoneConfiguration other) { - if (mUserId != other.mUserId) { - throw new IllegalArgumentException( - "Cannot merge configurations for different user IDs." - + " this.mUserId=" + this.mUserId - + ", other.mUserId=" + other.mUserId); - } + @NonNull + public Builder mergeProperties(@NonNull TimeZoneConfiguration other) { this.mBundle.putAll(other.mBundle); return this; } - Builder setPropertyBundleInternal(Bundle bundle) { + @NonNull + Builder setPropertyBundleInternal(@NonNull Bundle bundle) { this.mBundle.putAll(bundle); return this; } @@ -218,6 +227,7 @@ public final class TimeZoneConfiguration implements Parcelable { /** * Sets the state of the {@link #SETTING_AUTO_DETECTION_ENABLED} setting. */ + @NonNull public Builder setAutoDetectionEnabled(boolean enabled) { this.mBundle.putBoolean(SETTING_AUTO_DETECTION_ENABLED, enabled); return this; @@ -226,6 +236,7 @@ public final class TimeZoneConfiguration implements Parcelable { /** * Sets the state of the {@link #SETTING_GEO_DETECTION_ENABLED} setting. */ + @NonNull public Builder setGeoDetectionEnabled(boolean enabled) { this.mBundle.putBoolean(SETTING_GEO_DETECTION_ENABLED, enabled); return this; diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl index 4f7e1f62928a..af0389a14c4b 100644 --- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl +++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl @@ -16,11 +16,11 @@ package android.app.timezonedetector; -import android.app.timezonedetector.ITimeZoneConfigurationListener; +import android.app.time.ITimeZoneDetectorListener; +import android.app.time.TimeZoneCapabilitiesAndConfig; +import android.app.time.TimeZoneConfiguration; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; -import android.app.timezonedetector.TimeZoneCapabilities; -import android.app.timezonedetector.TimeZoneConfiguration; /** * System private API to communicate with time zone detector service. @@ -35,9 +35,9 @@ import android.app.timezonedetector.TimeZoneConfiguration; * {@hide} */ interface ITimeZoneDetectorService { - TimeZoneCapabilities getCapabilities(); - void addConfigurationListener(ITimeZoneConfigurationListener listener); - void removeConfigurationListener(ITimeZoneConfigurationListener listener); + TimeZoneCapabilitiesAndConfig getCapabilitiesAndConfig(); + void addListener(ITimeZoneDetectorListener listener); + void removeListener(ITimeZoneDetectorListener listener); boolean updateConfiguration(in TimeZoneConfiguration configuration); diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java index 2b1cbf259c55..486232d0f6ed 100644 --- a/core/java/android/app/timezonedetector/TimeZoneDetector.java +++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java @@ -30,70 +30,6 @@ import android.content.Context; public interface TimeZoneDetector { /** - * Returns the current user's time zone capabilities. See {@link TimeZoneCapabilities}. - */ - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - @NonNull - TimeZoneCapabilities getCapabilities(); - - /** - * Modifies the time zone detection configuration. - * - * <p>Configuration settings vary in scope: some may be global (affect all users), others may be - * specific to the current user. - * - * <p>The ability to modify configuration settings can be subject to restrictions. For - * example, they may be determined by device hardware, general policy (i.e. only the primary - * user can set them), or by a managed device policy. Use {@link #getCapabilities()} to obtain - * information at runtime about the user's capabilities. - * - * <p>Attempts to modify configuration settings with capabilities that are {@link - * TimeZoneCapabilities#CAPABILITY_NOT_SUPPORTED} or {@link - * TimeZoneCapabilities#CAPABILITY_NOT_ALLOWED} will have no effect and a {@code false} - * will be returned. Modifying configuration settings with capabilities that are {@link - * TimeZoneCapabilities#CAPABILITY_NOT_APPLICABLE} or {@link - * TimeZoneCapabilities#CAPABILITY_POSSESSED} will succeed. See {@link - * TimeZoneCapabilities} for further details. - * - * <p>If the supplied configuration only has some values set, then only the specified settings - * will be updated (where the user's capabilities allow) and other settings will be left - * unchanged. - * - * @return {@code true} if all the configuration settings specified have been set to the - * new values, {@code false} if none have - */ - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - boolean updateConfiguration(@NonNull TimeZoneConfiguration configuration); - - /** - * An interface that can be used to listen for changes to the time zone detector configuration. - */ - @FunctionalInterface - interface TimeZoneConfigurationListener { - /** - * Called when something about the time zone configuration on the device has changed. - * This could be because the current user has changed, one of the device's relevant settings - * has changed, or something that could affect a user's capabilities has changed. - * There are no guarantees about the thread used. - */ - void onChange(); - } - - /** - * Registers a listener that will be informed when something about the time zone configuration - * changes. - */ - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - void addConfigurationListener(@NonNull TimeZoneConfigurationListener listener); - - /** - * Removes a listener previously passed to - * {@link #addConfigurationListener(ITimeZoneConfigurationListener)} - */ - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - void removeConfigurationListener(@NonNull TimeZoneConfigurationListener listener); - - /** * A shared utility method to create a {@link ManualTimeZoneSuggestion}. * * @hide diff --git a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java index 4c69732abec9..3bd6b4bd692a 100644 --- a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java +++ b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java @@ -21,7 +21,6 @@ import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; -import android.util.ArraySet; import android.util.Log; /** @@ -35,108 +34,12 @@ public final class TimeZoneDetectorImpl implements TimeZoneDetector { private final ITimeZoneDetectorService mITimeZoneDetectorService; - private ITimeZoneConfigurationListener mConfigurationReceiver; - private ArraySet<TimeZoneConfigurationListener> mConfigurationListeners; - public TimeZoneDetectorImpl() throws ServiceNotFoundException { mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE)); } @Override - @NonNull - public TimeZoneCapabilities getCapabilities() { - if (DEBUG) { - Log.d(TAG, "getCapabilities called"); - } - try { - return mITimeZoneDetectorService.getCapabilities(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - @Override - public boolean updateConfiguration(@NonNull TimeZoneConfiguration configuration) { - if (DEBUG) { - Log.d(TAG, "updateConfiguration called: " + configuration); - } - try { - return mITimeZoneDetectorService.updateConfiguration(configuration); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - @Override - public void addConfigurationListener(@NonNull TimeZoneConfigurationListener listener) { - if (DEBUG) { - Log.d(TAG, "addConfigurationListener called: " + listener); - } - synchronized (this) { - if (mConfigurationListeners.contains(listener)) { - return; - } - if (mConfigurationReceiver == null) { - ITimeZoneConfigurationListener iListener = - new ITimeZoneConfigurationListener.Stub() { - @Override - public void onChange() { - notifyConfigurationListeners(); - } - }; - mConfigurationReceiver = iListener; - } - if (mConfigurationListeners == null) { - mConfigurationListeners = new ArraySet<>(); - } - - boolean wasEmpty = mConfigurationListeners.isEmpty(); - mConfigurationListeners.add(listener); - if (wasEmpty) { - try { - mITimeZoneDetectorService.addConfigurationListener(mConfigurationReceiver); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - } - } - - private void notifyConfigurationListeners() { - final ArraySet<TimeZoneConfigurationListener> configurationListeners; - synchronized (this) { - configurationListeners = new ArraySet<>(mConfigurationListeners); - } - int size = configurationListeners.size(); - for (int i = 0; i < size; i++) { - configurationListeners.valueAt(i).onChange(); - } - } - - @Override - public void removeConfigurationListener(@NonNull TimeZoneConfigurationListener listener) { - if (DEBUG) { - Log.d(TAG, "removeConfigurationListener called: " + listener); - } - - synchronized (this) { - if (mConfigurationListeners == null) { - return; - } - boolean wasEmpty = mConfigurationListeners.isEmpty(); - mConfigurationListeners.remove(listener); - if (mConfigurationListeners.isEmpty() && !wasEmpty) { - try { - mITimeZoneDetectorService.removeConfigurationListener(mConfigurationReceiver); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - } - } - - @Override public boolean suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) { if (DEBUG) { Log.d(TAG, "suggestManualTimeZone called: " + timeZoneSuggestion); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 005648ffec36..666ba32d0e4f 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -40,6 +40,7 @@ import android.app.ActivityManager; import android.app.IApplicationThread; import android.app.IServiceConnection; import android.app.VrManager; +import android.app.time.TimeManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -5090,6 +5091,14 @@ public abstract class Context { public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector"; /** + * Use with {@link #getSystemService(String)} to retrieve an {@link TimeManager}. + * @hide + * + * @see #getSystemService(String) + */ + public static final String TIME_MANAGER = "time_manager"; + + /** * Binder service name for {@link AppBindingService}. * @hide */ diff --git a/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java b/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java new file mode 100644 index 000000000000..01a25b27baf6 --- /dev/null +++ b/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java @@ -0,0 +1,160 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.time; + +import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED; +import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; + +import android.os.UserHandle; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class TimeZoneCapabilitiesTest { + + private static final UserHandle TEST_USER_HANDLE = UserHandle.of(12345); + + @Test + public void testEquals() { + TimeZoneCapabilities.Builder builder1 = new TimeZoneCapabilities.Builder(TEST_USER_HANDLE) + .setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED) + .setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED) + .setSuggestManualTimeZoneCapability(CAPABILITY_POSSESSED); + TimeZoneCapabilities.Builder builder2 = new TimeZoneCapabilities.Builder(TEST_USER_HANDLE) + .setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED) + .setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED) + .setSuggestManualTimeZoneCapability(CAPABILITY_POSSESSED); + { + TimeZoneCapabilities one = builder1.build(); + TimeZoneCapabilities two = builder2.build(); + assertEquals(one, two); + } + + builder2.setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED); + { + TimeZoneCapabilities one = builder1.build(); + TimeZoneCapabilities two = builder2.build(); + assertNotEquals(one, two); + } + + builder1.setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED); + { + TimeZoneCapabilities one = builder1.build(); + TimeZoneCapabilities two = builder2.build(); + assertEquals(one, two); + } + + builder2.setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED); + { + TimeZoneCapabilities one = builder1.build(); + TimeZoneCapabilities two = builder2.build(); + assertNotEquals(one, two); + } + + builder1.setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED); + { + TimeZoneCapabilities one = builder1.build(); + TimeZoneCapabilities two = builder2.build(); + assertEquals(one, two); + } + + builder2.setSuggestManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED); + { + TimeZoneCapabilities one = builder1.build(); + TimeZoneCapabilities two = builder2.build(); + assertNotEquals(one, two); + } + + builder1.setSuggestManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED); + { + TimeZoneCapabilities one = builder1.build(); + TimeZoneCapabilities two = builder2.build(); + assertEquals(one, two); + } + } + + @Test + public void testParcelable() { + TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(TEST_USER_HANDLE) + .setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED) + .setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED) + .setSuggestManualTimeZoneCapability(CAPABILITY_POSSESSED); + assertRoundTripParcelable(builder.build()); + + builder.setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED); + assertRoundTripParcelable(builder.build()); + + builder.setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED); + assertRoundTripParcelable(builder.build()); + + builder.setSuggestManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED); + assertRoundTripParcelable(builder.build()); + } + + @Test + public void testTryApplyConfigChanges_permitted() { + TimeZoneConfiguration oldConfiguration = + new TimeZoneConfiguration.Builder() + .setAutoDetectionEnabled(true) + .setGeoDetectionEnabled(true) + .build(); + TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(TEST_USER_HANDLE) + .setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED) + .setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED) + .setSuggestManualTimeZoneCapability(CAPABILITY_POSSESSED) + .build(); + + TimeZoneConfiguration configChange = new TimeZoneConfiguration.Builder() + .setAutoDetectionEnabled(false) + .build(); + + TimeZoneConfiguration expected = new TimeZoneConfiguration.Builder(oldConfiguration) + .setAutoDetectionEnabled(false) + .build(); + assertEquals(expected, capabilities.tryApplyConfigChanges(oldConfiguration, configChange)); + } + + @Test + public void testTryApplyConfigChanges_notPermitted() { + TimeZoneConfiguration oldConfiguration = + new TimeZoneConfiguration.Builder() + .setAutoDetectionEnabled(true) + .setGeoDetectionEnabled(true) + .build(); + TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(TEST_USER_HANDLE) + .setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED) + .setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED) + .setSuggestManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED) + .build(); + + TimeZoneConfiguration configChange = new TimeZoneConfiguration.Builder() + .setAutoDetectionEnabled(false) + .build(); + + assertNull(capabilities.tryApplyConfigChanges(oldConfiguration, configChange)); + } +} diff --git a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java b/core/tests/coretests/src/android/app/time/TimeZoneConfigurationTest.java index faf908de8d4a..3948eb86c4f6 100644 --- a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java +++ b/core/tests/coretests/src/android/app/time/TimeZoneConfigurationTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.app.timezonedetector; +package android.app.time; import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; @@ -23,16 +23,20 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Test; +import org.junit.runner.RunWith; +@RunWith(AndroidJUnit4.class) +@SmallTest public class TimeZoneConfigurationTest { - private static final int ARBITRARY_USER_ID = 9876; - @Test public void testBuilder_copyConstructor() { TimeZoneConfiguration.Builder builder1 = - new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + new TimeZoneConfiguration.Builder() .setAutoDetectionEnabled(true) .setGeoDetectionEnabled(true); TimeZoneConfiguration configuration1 = builder1.build(); @@ -45,27 +49,27 @@ public class TimeZoneConfigurationTest { @Test public void testIntrospectionMethods() { - TimeZoneConfiguration empty = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID).build(); + TimeZoneConfiguration empty = new TimeZoneConfiguration.Builder().build(); assertFalse(empty.isComplete()); - assertFalse(empty.hasSetting(TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED)); + assertFalse(empty.hasIsAutoDetectionEnabled()); - TimeZoneConfiguration completeConfig = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + TimeZoneConfiguration completeConfig = new TimeZoneConfiguration.Builder() .setAutoDetectionEnabled(true) .setGeoDetectionEnabled(true) .build(); assertTrue(completeConfig.isComplete()); - assertTrue(completeConfig.hasSetting(TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED)); + assertTrue(completeConfig.hasIsGeoDetectionEnabled()); } @Test public void testBuilder_mergeProperties() { - TimeZoneConfiguration configuration1 = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + TimeZoneConfiguration configuration1 = new TimeZoneConfiguration.Builder() .setAutoDetectionEnabled(true) .build(); { TimeZoneConfiguration mergedEmptyAnd1 = - new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + new TimeZoneConfiguration.Builder() .mergeProperties(configuration1) .build(); assertEquals(configuration1, mergedEmptyAnd1); @@ -73,7 +77,7 @@ public class TimeZoneConfigurationTest { { TimeZoneConfiguration configuration2 = - new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + new TimeZoneConfiguration.Builder() .setAutoDetectionEnabled(false) .build(); @@ -90,22 +94,14 @@ public class TimeZoneConfigurationTest { @Test public void testEquals() { TimeZoneConfiguration.Builder builder1 = - new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID); + new TimeZoneConfiguration.Builder(); { TimeZoneConfiguration one = builder1.build(); assertEquals(one, one); } - { - TimeZoneConfiguration.Builder differentUserBuilder = - new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID + 1); - TimeZoneConfiguration one = builder1.build(); - TimeZoneConfiguration two = differentUserBuilder.build(); - assertNotEquals(one, two); - } - TimeZoneConfiguration.Builder builder2 = - new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID); + new TimeZoneConfiguration.Builder(); { TimeZoneConfiguration one = builder1.build(); TimeZoneConfiguration two = builder2.build(); @@ -159,7 +155,7 @@ public class TimeZoneConfigurationTest { @Test public void testParcelable() { TimeZoneConfiguration.Builder builder = - new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID); + new TimeZoneConfiguration.Builder(); assertRoundTripParcelable(builder.build()); builder.setAutoDetectionEnabled(true); diff --git a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java deleted file mode 100644 index db127c6cb9ed..000000000000 --- a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.timezonedetector; - -import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNull; - -import org.junit.Test; - -public class TimeZoneCapabilitiesTest { - - private static final int ARBITRARY_USER_ID = 12345; - - @Test - public void testEquals() { - TimeZoneConfiguration configuration1 = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) - .setAutoDetectionEnabled(true) - .setGeoDetectionEnabled(true) - .build(); - TimeZoneConfiguration configuration2 = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) - .setAutoDetectionEnabled(false) - .setGeoDetectionEnabled(false) - .build(); - - TimeZoneCapabilities.Builder builder1 = new TimeZoneCapabilities.Builder() - .setConfiguration(configuration1) - .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED) - .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED) - .setSuggestManualTimeZone(CAPABILITY_POSSESSED); - TimeZoneCapabilities.Builder builder2 = new TimeZoneCapabilities.Builder() - .setConfiguration(configuration1) - .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED) - .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED) - .setSuggestManualTimeZone(CAPABILITY_POSSESSED); - { - TimeZoneCapabilities one = builder1.build(); - TimeZoneCapabilities two = builder2.build(); - assertEquals(one, two); - } - - builder2.setConfiguration(configuration2); - { - TimeZoneCapabilities one = builder1.build(); - TimeZoneCapabilities two = builder2.build(); - assertNotEquals(one, two); - } - - builder1.setConfiguration(configuration2); - { - TimeZoneCapabilities one = builder1.build(); - TimeZoneCapabilities two = builder2.build(); - assertEquals(one, two); - } - - builder2.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED); - { - TimeZoneCapabilities one = builder1.build(); - TimeZoneCapabilities two = builder2.build(); - assertNotEquals(one, two); - } - - builder1.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED); - { - TimeZoneCapabilities one = builder1.build(); - TimeZoneCapabilities two = builder2.build(); - assertEquals(one, two); - } - - builder2.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED); - { - TimeZoneCapabilities one = builder1.build(); - TimeZoneCapabilities two = builder2.build(); - assertNotEquals(one, two); - } - - builder1.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED); - { - TimeZoneCapabilities one = builder1.build(); - TimeZoneCapabilities two = builder2.build(); - assertEquals(one, two); - } - - builder2.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED); - { - TimeZoneCapabilities one = builder1.build(); - TimeZoneCapabilities two = builder2.build(); - assertNotEquals(one, two); - } - - builder1.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED); - { - TimeZoneCapabilities one = builder1.build(); - TimeZoneCapabilities two = builder2.build(); - assertEquals(one, two); - } - } - - @Test - public void testParcelable() { - TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) - .setAutoDetectionEnabled(true) - .setGeoDetectionEnabled(true) - .build(); - TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder() - .setConfiguration(configuration) - .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED) - .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED) - .setSuggestManualTimeZone(CAPABILITY_POSSESSED); - assertRoundTripParcelable(builder.build()); - - builder.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED); - assertRoundTripParcelable(builder.build()); - - builder.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED); - assertRoundTripParcelable(builder.build()); - - builder.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED); - assertRoundTripParcelable(builder.build()); - } - - @Test - public void testApplyUpdate_permitted() { - TimeZoneConfiguration oldConfiguration = - new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) - .setAutoDetectionEnabled(true) - .setGeoDetectionEnabled(true) - .build(); - TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder() - .setConfiguration(oldConfiguration) - .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED) - .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED) - .setSuggestManualTimeZone(CAPABILITY_POSSESSED) - .build(); - assertEquals(oldConfiguration, capabilities.getConfiguration()); - - TimeZoneConfiguration configChange = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) - .setAutoDetectionEnabled(false) - .build(); - - TimeZoneConfiguration expected = new TimeZoneConfiguration.Builder(oldConfiguration) - .setAutoDetectionEnabled(false) - .build(); - assertEquals(expected, capabilities.applyUpdate(configChange)); - } - - @Test - public void testApplyUpdate_notPermitted() { - TimeZoneConfiguration oldConfiguration = - new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) - .setAutoDetectionEnabled(true) - .setGeoDetectionEnabled(true) - .build(); - TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder() - .setConfiguration(oldConfiguration) - .setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED) - .setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED) - .setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED) - .build(); - assertEquals(oldConfiguration, capabilities.getConfiguration()); - - TimeZoneConfiguration configChange = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) - .setAutoDetectionEnabled(false) - .build(); - - assertNull(capabilities.applyUpdate(configChange)); - } -} diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java index b68c54fc6365..9e76bc19360f 100644 --- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java +++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java @@ -16,15 +16,16 @@ package com.android.server.timezonedetector; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED; +import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED; +import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED; import android.annotation.NonNull; import android.annotation.UserIdInt; -import android.app.timezonedetector.TimeZoneCapabilities; -import android.app.timezonedetector.TimeZoneConfiguration; +import android.app.time.TimeZoneCapabilities; +import android.app.time.TimeZoneCapabilitiesAndConfig; +import android.app.time.TimeZoneConfiguration; import android.os.UserHandle; import java.util.Objects; @@ -32,7 +33,7 @@ import java.util.Objects; /** * Holds all configuration values that affect time zone behavior and some associated logic, e.g. * {@link #getAutoDetectionEnabledBehavior()}, {@link #getGeoDetectionEnabledBehavior()} and {@link - * #createCapabilities()}. + * #createCapabilitiesAndConfig()}. */ public final class ConfigurationInternal { @@ -106,10 +107,15 @@ public final class ConfigurationInternal { return false; } - /** Creates a {@link TimeZoneCapabilities} object using the configuration values. */ - public TimeZoneCapabilities createCapabilities() { - TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder() - .setConfiguration(asConfiguration()); + /** Creates a {@link TimeZoneCapabilitiesAndConfig} object using the configuration values. */ + public TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig() { + return new TimeZoneCapabilitiesAndConfig(asCapabilities(), asConfiguration()); + } + + @NonNull + private TimeZoneCapabilities asCapabilities() { + UserHandle userHandle = UserHandle.of(mUserId); + TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(userHandle); boolean allowConfigDateTime = isUserConfigAllowed(); @@ -125,7 +131,7 @@ public final class ConfigurationInternal { } else { configureAutoDetectionEnabledCapability = CAPABILITY_POSSESSED; } - builder.setConfigureAutoDetectionEnabled(configureAutoDetectionEnabledCapability); + builder.setConfigureAutoDetectionEnabledCapability(configureAutoDetectionEnabledCapability); final int configureGeolocationDetectionEnabledCapability; if (!deviceHasTimeZoneDetection) { @@ -137,7 +143,8 @@ public final class ConfigurationInternal { } else { configureGeolocationDetectionEnabledCapability = CAPABILITY_POSSESSED; } - builder.setConfigureGeoDetectionEnabled(configureGeolocationDetectionEnabledCapability); + builder.setConfigureGeoDetectionEnabledCapability( + configureGeolocationDetectionEnabledCapability); // The ability to make manual time zone suggestions can also be restricted by policy. With // the current logic above, this could lead to a situation where a device hardware does not @@ -151,14 +158,14 @@ public final class ConfigurationInternal { } else { suggestManualTimeZoneCapability = CAPABILITY_POSSESSED; } - builder.setSuggestManualTimeZone(suggestManualTimeZoneCapability); + builder.setSuggestManualTimeZoneCapability(suggestManualTimeZoneCapability); return builder.build(); } /** Returns a {@link TimeZoneConfiguration} from the configuration values. */ - public TimeZoneConfiguration asConfiguration() { - return new TimeZoneConfiguration.Builder(mUserId) + private TimeZoneConfiguration asConfiguration() { + return new TimeZoneConfiguration.Builder() .setAutoDetectionEnabled(getAutoDetectionEnabledSetting()) .setGeoDetectionEnabled(getGeoDetectionEnabledSetting()) .build(); @@ -171,10 +178,10 @@ public final class ConfigurationInternal { */ public ConfigurationInternal merge(TimeZoneConfiguration newConfiguration) { Builder builder = new Builder(this); - if (newConfiguration.hasSetting(TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED)) { + if (newConfiguration.hasIsAutoDetectionEnabled()) { builder.setAutoDetectionEnabled(newConfiguration.isAutoDetectionEnabled()); } - if (newConfiguration.hasSetting(TimeZoneConfiguration.SETTING_GEO_DETECTION_ENABLED)) { + if (newConfiguration.hasIsGeoDetectionEnabled()) { builder.setGeoDetectionEnabled(newConfiguration.isGeoDetectionEnabled()); } return builder.build(); diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java index 0a4f5474e4b4..964dbecf705c 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java @@ -23,7 +23,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; import android.app.AlarmManager; -import android.app.timezonedetector.TimeZoneConfiguration; +import android.app.time.TimeZoneConfiguration; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -159,7 +159,7 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat } @Override - public void storeConfiguration(TimeZoneConfiguration configuration) { + public void storeConfiguration(@UserIdInt int userId, TimeZoneConfiguration configuration) { Objects.requireNonNull(configuration); // Avoid writing the auto detection enabled setting for devices that do not support auto @@ -171,7 +171,6 @@ public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrat setAutoDetectionEnabled(autoDetectionEnabled); if (mGeoDetectionFeatureEnabled) { - final int userId = configuration.getUserId(); final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled(); setGeoDetectionEnabled(userId, geoTzDetectionEnabled); } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index b72cb546aae4..6e1f89b3919d 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java @@ -18,18 +18,19 @@ package com.android.server.timezonedetector; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.timezonedetector.ITimeZoneConfigurationListener; +import android.app.time.ITimeZoneDetectorListener; +import android.app.time.TimeZoneCapabilitiesAndConfig; +import android.app.time.TimeZoneConfiguration; import android.app.timezonedetector.ITimeZoneDetectorService; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; -import android.app.timezonedetector.TimeZoneCapabilities; -import android.app.timezonedetector.TimeZoneConfiguration; import android.content.Context; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; +import android.util.ArrayMap; import android.util.IndentingPrintWriter; import android.util.Slog; @@ -41,7 +42,6 @@ import com.android.server.SystemService; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.Objects; /** @@ -109,10 +109,14 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub @NonNull private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy; - @GuardedBy("mConfigurationListeners") + /** + * Holds the listeners. The key is the {@link IBinder} associated with the listener, the value + * is the listener itself. + */ + @GuardedBy("mListeners") @NonNull - private final ArrayList<ITimeZoneConfigurationListener> mConfigurationListeners = - new ArrayList<>(); + private final ArrayMap<IBinder, ITimeZoneDetectorListener> mListeners = + new ArrayMap<>(); private static TimeZoneDetectorService create( @NonNull Context context, @NonNull Handler handler, @@ -133,20 +137,22 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub mCallerIdentityInjector = Objects.requireNonNull(callerIdentityInjector); mTimeZoneDetectorStrategy = Objects.requireNonNull(timeZoneDetectorStrategy); - // Wire up a change listener so that ITimeZoneConfigurationListeners can be notified when + // Wire up a change listener so that ITimeZoneDetectorListeners can be notified when // the configuration changes for any reason. mTimeZoneDetectorStrategy.addConfigChangeListener(this::handleConfigurationChanged); } @Override @NonNull - public TimeZoneCapabilities getCapabilities() { - enforceManageTimeZoneDetectorConfigurationPermission(); + public TimeZoneCapabilitiesAndConfig getCapabilitiesAndConfig() { + enforceManageTimeZoneDetectorPermission(); int userId = mCallerIdentityInjector.getCallingUserId(); long token = mCallerIdentityInjector.clearCallingIdentity(); try { - return mTimeZoneDetectorStrategy.getConfigurationInternal(userId).createCapabilities(); + ConfigurationInternal configurationInternal = + mTimeZoneDetectorStrategy.getConfigurationInternal(userId); + return configurationInternal.createCapabilitiesAndConfig(); } finally { mCallerIdentityInjector.restoreCallingIdentity(token); } @@ -154,37 +160,34 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub @Override public boolean updateConfiguration(@NonNull TimeZoneConfiguration configuration) { - enforceManageTimeZoneDetectorConfigurationPermission(); + enforceManageTimeZoneDetectorPermission(); Objects.requireNonNull(configuration); int callingUserId = mCallerIdentityInjector.getCallingUserId(); - if (callingUserId != configuration.getUserId()) { - return false; - } - long token = mCallerIdentityInjector.clearCallingIdentity(); try { - return mTimeZoneDetectorStrategy.updateConfiguration(configuration); + return mTimeZoneDetectorStrategy.updateConfiguration(callingUserId, configuration); } finally { mCallerIdentityInjector.restoreCallingIdentity(token); } } @Override - public void addConfigurationListener(@NonNull ITimeZoneConfigurationListener listener) { - enforceManageTimeZoneDetectorConfigurationPermission(); + public void addListener(@NonNull ITimeZoneDetectorListener listener) { + enforceManageTimeZoneDetectorPermission(); Objects.requireNonNull(listener); - synchronized (mConfigurationListeners) { - if (mConfigurationListeners.contains(listener)) { + synchronized (mListeners) { + IBinder listenerBinder = listener.asBinder(); + if (mListeners.containsKey(listenerBinder)) { return; } try { // Ensure the reference to the listener will be removed if the client process dies. - listener.asBinder().linkToDeath(this, 0 /* flags */); + listenerBinder.linkToDeath(this, 0 /* flags */); // Only add the listener if we can linkToDeath(). - mConfigurationListeners.add(listener); + mListeners.put(listenerBinder, listener); } catch (RemoteException e) { Slog.e(TAG, "Unable to linkToDeath() for listener=" + listener, e); } @@ -192,21 +195,22 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub } @Override - public void removeConfigurationListener(@NonNull ITimeZoneConfigurationListener listener) { - enforceManageTimeZoneDetectorConfigurationPermission(); + public void removeListener(@NonNull ITimeZoneDetectorListener listener) { + enforceManageTimeZoneDetectorPermission(); Objects.requireNonNull(listener); - synchronized (mConfigurationListeners) { + synchronized (mListeners) { + IBinder listenerBinder = listener.asBinder(); boolean removedListener = false; - if (mConfigurationListeners.remove(listener)) { + if (mListeners.remove(listenerBinder) != null) { // Stop listening for the client process to die. - listener.asBinder().unlinkToDeath(this, 0 /* flags */); + listenerBinder.unlinkToDeath(this, 0 /* flags */); removedListener = true; } if (!removedListener) { Slog.w(TAG, "Client asked to remove listener=" + listener + ", but no listeners were removed." - + " mConfigurationListeners=" + mConfigurationListeners); + + " mListeners=" + mListeners); } } } @@ -218,19 +222,18 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub } /** - * Called when one of the ITimeZoneConfigurationListener processes dies before calling - * {@link #removeConfigurationListener(ITimeZoneConfigurationListener)}. + * Called when one of the ITimeZoneDetectorListener processes dies before calling + * {@link #removeListener(ITimeZoneDetectorListener)}. */ @Override public void binderDied(IBinder who) { - synchronized (mConfigurationListeners) { + synchronized (mListeners) { boolean removedListener = false; - final int listenerCount = mConfigurationListeners.size(); + final int listenerCount = mListeners.size(); for (int listenerIndex = listenerCount - 1; listenerIndex >= 0; listenerIndex--) { - ITimeZoneConfigurationListener listener = - mConfigurationListeners.get(listenerIndex); - if (listener.asBinder().equals(who)) { - mConfigurationListeners.remove(listenerIndex); + IBinder listenerBinder = mListeners.keyAt(listenerIndex); + if (listenerBinder.equals(who)) { + mListeners.removeAt(listenerIndex); removedListener = true; break; } @@ -238,7 +241,7 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub if (!removedListener) { Slog.w(TAG, "Notified of binder death for who=" + who + ", but did not remove any listeners." - + " mConfigurationListeners=" + mConfigurationListeners); + + " mConfigurationListeners=" + mListeners); } } } @@ -247,11 +250,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub // Configuration has changed, but each user may have a different view of the configuration. // It's possible that this will cause unnecessary notifications but that shouldn't be a // problem. - synchronized (mConfigurationListeners) { - final int listenerCount = mConfigurationListeners.size(); + synchronized (mListeners) { + final int listenerCount = mListeners.size(); for (int listenerIndex = 0; listenerIndex < listenerCount; listenerIndex++) { - ITimeZoneConfigurationListener listener = - mConfigurationListeners.get(listenerIndex); + ITimeZoneDetectorListener listener = mListeners.valueAt(listenerIndex); try { listener.onChange(); } catch (RemoteException e) { @@ -302,11 +304,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub ipw.flush(); } - private void enforceManageTimeZoneDetectorConfigurationPermission() { - // TODO Switch to a dedicated MANAGE_TIME_AND_ZONE_CONFIGURATION permission. + private void enforceManageTimeZoneDetectorPermission() { mContext.enforceCallingPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS, - "manage time and time zone configuration"); + android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION, + "manage time and time zone detection"); } private void enforceSuggestGeolocationTimeZonePermission() { diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java index f944c5638fa9..0b1d6d71ea7b 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java @@ -17,9 +17,9 @@ package com.android.server.timezonedetector; import android.annotation.NonNull; import android.annotation.UserIdInt; +import android.app.time.TimeZoneConfiguration; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; -import android.app.timezonedetector.TimeZoneConfiguration; import android.util.IndentingPrintWriter; /** @@ -96,7 +96,8 @@ public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container { * <p>This method returns {@code true} if the configuration was changed, * {@code false} otherwise. */ - boolean updateConfiguration(@NonNull TimeZoneConfiguration configuration); + boolean updateConfiguration( + @UserIdInt int userId, @NonNull TimeZoneConfiguration configuration); /** * Suggests zero, one or more time zones 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 743baedb00e1..781668bebbc9 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java @@ -15,20 +15,21 @@ */ package com.android.server.timezonedetector; +import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED; import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID; import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY; import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS; import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET; import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.time.TimeZoneCapabilities; +import android.app.time.TimeZoneCapabilitiesAndConfig; +import android.app.time.TimeZoneConfiguration; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; -import android.app.timezonedetector.TimeZoneCapabilities; -import android.app.timezonedetector.TimeZoneConfiguration; import android.content.Context; import android.os.Handler; import android.util.IndentingPrintWriter; @@ -93,7 +94,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat * All checks about user capabilities must be done by the caller and * {@link TimeZoneConfiguration#isComplete()} must be {@code true}. */ - void storeConfiguration(TimeZoneConfiguration newConfiguration); + void storeConfiguration(@UserIdInt int userId, TimeZoneConfiguration newConfiguration); } private static final String LOG_TAG = "TimeZoneDetectorStrategy"; @@ -240,27 +241,26 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat } @Override - public synchronized boolean updateConfiguration( + public synchronized boolean updateConfiguration(@UserIdInt int userId, @NonNull TimeZoneConfiguration requestedConfiguration) { Objects.requireNonNull(requestedConfiguration); - int userId = requestedConfiguration.getUserId(); - TimeZoneCapabilities capabilities = getConfigurationInternal(userId).createCapabilities(); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = + getConfigurationInternal(userId).createCapabilitiesAndConfig(); + TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); + TimeZoneConfiguration oldConfiguration = capabilitiesAndConfig.getConfiguration(); - // Create a new configuration builder, and copy across the mutable properties users are - // able to modify. Other properties are therefore ignored. final TimeZoneConfiguration newConfiguration = - capabilities.applyUpdate(requestedConfiguration); + capabilities.tryApplyConfigChanges(oldConfiguration, requestedConfiguration); if (newConfiguration == null) { - // The changes could not be made due to + // The changes could not be made because the user's capabilities do not allow it. return false; } // Store the configuration / notify as needed. This will cause the mCallback to invoke // handleConfigChanged() asynchronously. - mCallback.storeConfiguration(newConfiguration); + mCallback.storeConfiguration(userId, newConfiguration); - TimeZoneConfiguration oldConfiguration = capabilities.getConfiguration(); String logMsg = "Configuration changed:" + " oldConfiguration=" + oldConfiguration + ", newConfiguration=" + newConfiguration; @@ -313,8 +313,10 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat String timeZoneId = suggestion.getZoneId(); String cause = "Manual time suggestion received: suggestion=" + suggestion; - TimeZoneCapabilities capabilities = getConfigurationInternal(userId).createCapabilities(); - if (capabilities.getSuggestManualTimeZone() != CAPABILITY_POSSESSED) { + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = + getConfigurationInternal(userId).createCapabilitiesAndConfig(); + TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); + if (capabilities.getSuggestManualTimeZoneCapability() != CAPABILITY_POSSESSED) { Slog.i(LOG_TAG, "User does not have the capability needed to set the time zone manually" + ", capabilities=" + capabilities + ", timeZoneId=" + timeZoneId @@ -605,7 +607,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat ipw.println("mCallback.getCurrentUserId()=" + currentUserId); ConfigurationInternal configuration = mCallback.getConfigurationInternal(currentUserId); ipw.println("mCallback.getConfiguration(currentUserId)=" + configuration); - ipw.println("[Capabilities=" + configuration.createCapabilities() + "]"); + ipw.println("[Capabilities=" + configuration.createCapabilitiesAndConfig() + "]"); ipw.println("mCallback.isDeviceTimeZoneInitialized()=" + mCallback.isDeviceTimeZoneInitialized()); ipw.println("mCallback.getDeviceTimeZone()=" + mCallback.getDeviceTimeZone()); 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 d7ed96fd5833..54b5bee9a6ab 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java @@ -16,22 +16,23 @@ package com.android.server.timezonedetector; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED; -import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED; +import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED; +import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import android.app.timezonedetector.TimeZoneCapabilities; +import android.app.time.TimeZoneCapabilities; +import android.app.time.TimeZoneCapabilitiesAndConfig; +import android.app.time.TimeZoneConfiguration; import org.junit.Test; /** - * Tests for {@link ConfigurationInternal} and the {@link TimeZoneCapabilities} and - * {@link android.app.timezonedetector.TimeZoneConfiguration} that can be generated from it. + * Tests for {@link ConfigurationInternal} and the {@link TimeZoneCapabilitiesAndConfig}. */ public class ConfigurationInternalTest { @@ -59,13 +60,20 @@ public class ConfigurationInternalTest { assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior()); assertTrue(autoOnConfig.getGeoDetectionEnabledBehavior()); - TimeZoneCapabilities capabilities = autoOnConfig.createCapabilities(); - assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled()); - assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled()); - assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSuggestManualTimeZone()); - assertEquals(autoOnConfig.asConfiguration(), capabilities.getConfiguration()); - assertTrue(capabilities.getConfiguration().isAutoDetectionEnabled()); - assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled()); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = + autoOnConfig.createCapabilitiesAndConfig(); + + TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); + assertEquals(CAPABILITY_POSSESSED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_POSSESSED, + capabilities.getConfigureGeoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_APPLICABLE, + capabilities.getSuggestManualTimeZoneCapability()); + + TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration(); + assertTrue(configuration.isAutoDetectionEnabled()); + assertTrue(configuration.isGeoDetectionEnabled()); } { @@ -77,13 +85,20 @@ public class ConfigurationInternalTest { assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior()); assertFalse(autoOffConfig.getGeoDetectionEnabledBehavior()); - TimeZoneCapabilities capabilities = autoOffConfig.createCapabilities(); - assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled()); - assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled()); - assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone()); - assertEquals(autoOffConfig.asConfiguration(), capabilities.getConfiguration()); - assertFalse(capabilities.getConfiguration().isAutoDetectionEnabled()); - assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled()); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = + autoOffConfig.createCapabilitiesAndConfig(); + + TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); + assertEquals(CAPABILITY_POSSESSED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_POSSESSED, + capabilities.getConfigureGeoDetectionEnabledCapability()); + assertEquals(CAPABILITY_POSSESSED, + capabilities.getSuggestManualTimeZoneCapability()); + + TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration(); + assertFalse(configuration.isAutoDetectionEnabled()); + assertTrue(configuration.isGeoDetectionEnabled()); } } @@ -106,13 +121,20 @@ public class ConfigurationInternalTest { assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior()); assertTrue(autoOnConfig.getGeoDetectionEnabledBehavior()); - TimeZoneCapabilities capabilities = autoOnConfig.createCapabilities(); - assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled()); - assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled()); - assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone()); - assertEquals(autoOnConfig.asConfiguration(), capabilities.getConfiguration()); - assertTrue(capabilities.getConfiguration().isAutoDetectionEnabled()); - assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled()); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = + autoOnConfig.createCapabilitiesAndConfig(); + + TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); + assertEquals(CAPABILITY_NOT_ALLOWED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_ALLOWED, + capabilities.getConfigureGeoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_ALLOWED, + capabilities.getSuggestManualTimeZoneCapability()); + + TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration(); + assertTrue(configuration.isAutoDetectionEnabled()); + assertTrue(configuration.isGeoDetectionEnabled()); } { @@ -124,13 +146,20 @@ public class ConfigurationInternalTest { assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior()); assertFalse(autoOffConfig.getGeoDetectionEnabledBehavior()); - TimeZoneCapabilities capabilities = autoOffConfig.createCapabilities(); - assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled()); - assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled()); - assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone()); - assertEquals(autoOffConfig.asConfiguration(), capabilities.getConfiguration()); - assertFalse(capabilities.getConfiguration().isAutoDetectionEnabled()); - assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled()); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = + autoOffConfig.createCapabilitiesAndConfig(); + + TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); + assertEquals(CAPABILITY_NOT_ALLOWED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_ALLOWED, + capabilities.getConfigureGeoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_ALLOWED, + capabilities.getSuggestManualTimeZoneCapability()); + + TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration(); + assertFalse(configuration.isAutoDetectionEnabled()); + assertTrue(configuration.isGeoDetectionEnabled()); } } @@ -153,13 +182,19 @@ public class ConfigurationInternalTest { assertFalse(autoOnConfig.getAutoDetectionEnabledBehavior()); assertFalse(autoOnConfig.getGeoDetectionEnabledBehavior()); - TimeZoneCapabilities capabilities = autoOnConfig.createCapabilities(); - assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled()); - assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled()); - assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone()); - assertEquals(autoOnConfig.asConfiguration(), capabilities.getConfiguration()); - assertTrue(capabilities.getConfiguration().isAutoDetectionEnabled()); - assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled()); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = + autoOnConfig.createCapabilitiesAndConfig(); + + TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); + assertEquals(CAPABILITY_NOT_SUPPORTED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_SUPPORTED, + capabilities.getConfigureGeoDetectionEnabledCapability()); + assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZoneCapability()); + + TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration(); + assertTrue(configuration.isAutoDetectionEnabled()); + assertTrue(configuration.isGeoDetectionEnabled()); } { ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig) @@ -170,13 +205,19 @@ public class ConfigurationInternalTest { assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior()); assertFalse(autoOffConfig.getGeoDetectionEnabledBehavior()); - TimeZoneCapabilities capabilities = autoOffConfig.createCapabilities(); - assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled()); - assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled()); - assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone()); - assertEquals(autoOffConfig.asConfiguration(), capabilities.getConfiguration()); - assertFalse(capabilities.getConfiguration().isAutoDetectionEnabled()); - assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled()); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = + autoOffConfig.createCapabilitiesAndConfig(); + + TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); + assertEquals(CAPABILITY_NOT_SUPPORTED, + capabilities.getConfigureAutoDetectionEnabledCapability()); + assertEquals(CAPABILITY_NOT_SUPPORTED, + capabilities.getConfigureGeoDetectionEnabledCapability()); + assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZoneCapability()); + + TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration(); + assertFalse(configuration.isAutoDetectionEnabled()); + assertTrue(configuration.isGeoDetectionEnabled()); } } } 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 4ef20829f2dc..bad380acf4b3 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java @@ -22,10 +22,11 @@ import static org.junit.Assert.fail; import android.annotation.NonNull; import android.annotation.UserIdInt; +import android.app.time.TimeZoneCapabilities; +import android.app.time.TimeZoneCapabilitiesAndConfig; +import android.app.time.TimeZoneConfiguration; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; -import android.app.timezonedetector.TimeZoneCapabilities; -import android.app.timezonedetector.TimeZoneConfiguration; import android.util.IndentingPrintWriter; import java.util.ArrayList; @@ -67,20 +68,25 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { } @Override - public boolean updateConfiguration(@NonNull TimeZoneConfiguration requestedChanges) { + public boolean updateConfiguration( + @UserIdInt int userID, @NonNull TimeZoneConfiguration requestedChanges) { assertNotNull(mConfigurationInternal); assertNotNull(requestedChanges); // Simulate the real strategy's behavior: the new configuration will be updated to be the // 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. - TimeZoneCapabilities capabilities = mConfigurationInternal.createCapabilities(); - TimeZoneConfiguration newConfiguration = capabilities.applyUpdate(requestedChanges); + TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = + mConfigurationInternal.createCapabilitiesAndConfig(); + TimeZoneCapabilities capabilities = capabilitiesAndConfig.getCapabilities(); + TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration(); + TimeZoneConfiguration newConfiguration = + capabilities.tryApplyConfigChanges(configuration, requestedChanges); if (newConfiguration == null) { return false; } - if (!newConfiguration.equals(capabilities.getConfiguration())) { + if (!newConfiguration.equals(capabilitiesAndConfig.getConfiguration())) { mConfigurationInternal = mConfigurationInternal.merge(newConfiguration); // Note: Unlike the real strategy, the listeners is invoked synchronously. 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 27b04b6ab17d..cb27657f8364 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java @@ -31,10 +31,10 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import android.app.timezonedetector.ITimeZoneConfigurationListener; +import android.app.time.ITimeZoneDetectorListener; +import android.app.time.TimeZoneConfiguration; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; -import android.app.timezonedetector.TimeZoneConfiguration; import android.content.Context; import android.content.pm.PackageManager; import android.os.HandlerThread; @@ -91,85 +91,85 @@ public class TimeZoneDetectorServiceTest { } @Test(expected = SecurityException.class) - public void testGetCapabilities_withoutPermission() { + public void testGetCapabilitiesAndConfig_withoutPermission() { doThrow(new SecurityException("Mock")) .when(mMockContext).enforceCallingPermission(anyString(), any()); try { - mTimeZoneDetectorService.getCapabilities(); + mTimeZoneDetectorService.getCapabilitiesAndConfig(); fail(); } finally { verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.WRITE_SECURE_SETTINGS), + eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); } } @Test - public void testGetCapabilities() { + public void testGetCapabilitiesAndConfig() { doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); ConfigurationInternal configuration = createConfigurationInternal(true /* autoDetectionEnabled*/); mFakeTimeZoneDetectorStrategy.initializeConfiguration(configuration); - assertEquals(configuration.createCapabilities(), - mTimeZoneDetectorService.getCapabilities()); + assertEquals(configuration.createCapabilitiesAndConfig(), + mTimeZoneDetectorService.getCapabilitiesAndConfig()); verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.WRITE_SECURE_SETTINGS), + eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); } @Test(expected = SecurityException.class) - public void testAddConfigurationListener_withoutPermission() { + public void testAddListener_withoutPermission() { doThrow(new SecurityException("Mock")) .when(mMockContext).enforceCallingPermission(anyString(), any()); - ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class); + ITimeZoneDetectorListener mockListener = mock(ITimeZoneDetectorListener.class); try { - mTimeZoneDetectorService.addConfigurationListener(mockListener); + mTimeZoneDetectorService.addListener(mockListener); fail(); } finally { verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.WRITE_SECURE_SETTINGS), + eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); } } @Test(expected = SecurityException.class) - public void testRemoveConfigurationListener_withoutPermission() { + public void testRemoveListener_withoutPermission() { doThrow(new SecurityException("Mock")) .when(mMockContext).enforceCallingPermission(anyString(), any()); - ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class); + ITimeZoneDetectorListener mockListener = mock(ITimeZoneDetectorListener.class); try { - mTimeZoneDetectorService.removeConfigurationListener(mockListener); + mTimeZoneDetectorService.removeListener(mockListener); fail("Expected a SecurityException"); } finally { verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.WRITE_SECURE_SETTINGS), + eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); } } @Test - public void testConfigurationChangeListenerRegistrationAndCallbacks() throws Exception { + public void testListenerRegistrationAndCallbacks() throws Exception { ConfigurationInternal initialConfiguration = createConfigurationInternal(false /* autoDetectionEnabled */); mFakeTimeZoneDetectorStrategy.initializeConfiguration(initialConfiguration); IBinder mockListenerBinder = mock(IBinder.class); - ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class); + ITimeZoneDetectorListener mockListener = mock(ITimeZoneDetectorListener.class); { doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); when(mockListener.asBinder()).thenReturn(mockListenerBinder); - mTimeZoneDetectorService.addConfigurationListener(mockListener); + mTimeZoneDetectorService.addListener(mockListener); verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.WRITE_SECURE_SETTINGS), + eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); verify(mockListener).asBinder(); verify(mockListenerBinder).linkToDeath(any(), anyInt()); @@ -186,7 +186,7 @@ public class TimeZoneDetectorServiceTest { mTimeZoneDetectorService.updateConfiguration(autoDetectEnabledConfiguration); verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.WRITE_SECURE_SETTINGS), + eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); verify(mockListener).onChange(); verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext); @@ -200,10 +200,10 @@ public class TimeZoneDetectorServiceTest { // Now remove the listener, change the config again, and verify the listener is not // called. - mTimeZoneDetectorService.removeConfigurationListener(mockListener); + mTimeZoneDetectorService.removeListener(mockListener); verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.WRITE_SECURE_SETTINGS), + eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); verify(mockListener).asBinder(); verify(mockListenerBinder).unlinkToDeath(any(), eq(0)); @@ -219,7 +219,7 @@ public class TimeZoneDetectorServiceTest { mTimeZoneDetectorService.updateConfiguration(autoDetectDisabledConfiguration); verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.WRITE_SECURE_SETTINGS), + eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString()); verify(mockListener, never()).onChange(); verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext); @@ -354,7 +354,7 @@ public class TimeZoneDetectorServiceTest { } private static TimeZoneConfiguration createTimeZoneConfiguration(boolean autoDetectionEnabled) { - return new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID) + return new TimeZoneConfiguration.Builder() .setAutoDetectionEnabled(autoDetectionEnabled) .build(); } 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 1cdf19319209..296aa73f9c45 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java @@ -39,11 +39,11 @@ import static org.junit.Assert.fail; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.app.time.TimeZoneConfiguration; import android.app.timezonedetector.ManualTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.app.timezonedetector.TelephonyTimeZoneSuggestion.MatchType; import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality; -import android.app.timezonedetector.TimeZoneConfiguration; import android.util.IndentingPrintWriter; import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion; @@ -186,26 +186,27 @@ public class TimeZoneDetectorStrategyImplTest { Script script = new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED); // Set the configuration with auto detection enabled. - script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */); + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */); // Nothing should have happened: it was initialized in this state. script.verifyConfigurationNotChanged(); // Update the configuration with auto detection disabled. - script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */); + script.simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */); // The settings should have been changed and the StrategyListener onChange() called. script.verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED); // Update the configuration with auto detection enabled. - script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */); + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */); // The settings should have been changed and the StrategyListener onChange() called. script.verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED); // Update the configuration to enable geolocation time zone detection. script.simulateUpdateConfiguration( - CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */); + USER_ID, CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */); // The settings should have been changed and the StrategyListener onChange() called. script.verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED); @@ -216,20 +217,22 @@ public class TimeZoneDetectorStrategyImplTest { Script script = new Script().initializeConfig(CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED); // Try to update the configuration with auto detection disabled. - script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, false /* expectedResult */); + script.simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_DISABLED, false /* expectedResult */); // The settings should not have been changed: user shouldn't have the capabilities. script.verifyConfigurationNotChanged(); // Update the configuration with auto detection enabled. - script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, false /* expectedResult */); + script.simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_ENABLED, false /* expectedResult */); // The settings should not have been changed: user shouldn't have the capabilities. script.verifyConfigurationNotChanged(); // Try to update the configuration to enable geolocation time zone detection. script.simulateUpdateConfiguration( - CONFIG_GEO_DETECTION_ENABLED, false /* expectedResult */); + USER_ID, CONFIG_GEO_DETECTION_ENABLED, false /* expectedResult */); // The settings should not have been changed: user shouldn't have the capabilities. script.verifyConfigurationNotChanged(); @@ -240,13 +243,15 @@ public class TimeZoneDetectorStrategyImplTest { Script script = new Script().initializeConfig(CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED); // Try to update the configuration with auto detection disabled. - script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, false /* expectedResult */); + script.simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_DISABLED, false /* expectedResult */); // The settings should not have been changed: user shouldn't have the capabilities. script.verifyConfigurationNotChanged(); // Update the configuration with auto detection enabled. - script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, false /* expectedResult */); + script.simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_ENABLED, false /* expectedResult */); // The settings should not have been changed: user shouldn't have the capabilities. script.verifyConfigurationNotChanged(); @@ -389,7 +394,8 @@ public class TimeZoneDetectorStrategyImplTest { mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); // Toggling the time zone setting on should cause the device setting to be set. - script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */); + script.simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */); // When time zone detection is already enabled the suggestion (if it scores highly // enough) should be set immediately. @@ -406,7 +412,8 @@ public class TimeZoneDetectorStrategyImplTest { mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); // Toggling the time zone setting should off should do nothing. - script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */) + script.simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) .verifyTimeZoneNotChanged(); // Assert internal service state. @@ -588,18 +595,20 @@ public class TimeZoneDetectorStrategyImplTest { // Toggling time zone detection should set the device time zone only if the current setting // value is different from the most recent telephony suggestion. - script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */) + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) .verifyTimeZoneNotChanged() - .simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */) + .simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) .verifyTimeZoneNotChanged(); // Simulate a user turning auto detection off, a new suggestion being made while auto // detection is off, and the user turning it on again. - script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */) + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */) .simulateTelephonyTimeZoneSuggestion(newYorkSuggestion) .verifyTimeZoneNotChanged(); // Latest suggestion should be used. - script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */) + script.simulateUpdateConfiguration( + USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) .verifyTimeZoneChangedAndReset(newYorkSuggestion); } @@ -784,7 +793,7 @@ public class TimeZoneDetectorStrategyImplTest { assertEquals(suggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); // Turn off geo detection and verify the latest suggestion is cleared. - script.simulateUpdateConfiguration(CONFIG_GEO_DETECTION_DISABLED, true) + script.simulateUpdateConfiguration(USER_ID, CONFIG_GEO_DETECTION_DISABLED, true) .verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED); // Assert internal service state. @@ -824,19 +833,21 @@ public class TimeZoneDetectorStrategyImplTest { // Toggling the time zone detection enabled setting on should cause the device setting to be // set from the telephony signal, as we've started with geolocation time zone detection // disabled. - script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */) + script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */) .verifyTimeZoneChangedAndReset(telephonySuggestion); // Changing the detection to enable geo detection won't cause the device tz setting to // change because the geo suggestion is empty. - script.simulateUpdateConfiguration(CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */) + script.simulateUpdateConfiguration( + USER_ID, CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */) .verifyTimeZoneNotChanged() .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion) .verifyTimeZoneChangedAndReset(geolocationSuggestion.getZoneIds().get(0)); // Changing the detection to disable geo detection should cause the device tz setting to // change to the telephony suggestion. - script.simulateUpdateConfiguration(CONFIG_GEO_DETECTION_DISABLED, true /* expectedResult */) + script.simulateUpdateConfiguration( + USER_ID, CONFIG_GEO_DETECTION_DISABLED, true /* expectedResult */) .verifyTimeZoneChangedAndReset(telephonySuggestion); assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion()); @@ -898,7 +909,7 @@ public class TimeZoneDetectorStrategyImplTest { private static TimeZoneConfiguration createConfig( @Nullable Boolean autoDetection, @Nullable Boolean geoDetection) { - TimeZoneConfiguration.Builder builder = new TimeZoneConfiguration.Builder(USER_ID); + TimeZoneConfiguration.Builder builder = new TimeZoneConfiguration.Builder(); if (autoDetection != null) { builder.setAutoDetectionEnabled(autoDetection); } @@ -957,9 +968,10 @@ public class TimeZoneDetectorStrategyImplTest { } @Override - public void storeConfiguration(TimeZoneConfiguration newConfiguration) { + public void storeConfiguration( + @UserIdInt int userId, TimeZoneConfiguration newConfiguration) { ConfigurationInternal oldConfiguration = mConfigurationInternal.getLatest(); - if (newConfiguration.getUserId() != oldConfiguration.getUserId()) { + if (userId != oldConfiguration.getUserId()) { fail("FakeCallback does not support multiple users"); } @@ -1014,9 +1026,9 @@ public class TimeZoneDetectorStrategyImplTest { * the return value. */ Script simulateUpdateConfiguration( - TimeZoneConfiguration configuration, boolean expectedResult) { + int userId, TimeZoneConfiguration configuration, boolean expectedResult) { assertEquals(expectedResult, - mTimeZoneDetectorStrategy.updateConfiguration(configuration)); + mTimeZoneDetectorStrategy.updateConfiguration(userId, configuration)); return this; } |