diff options
26 files changed, 1092 insertions, 70 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 40d303723099..3423b2bd5e18 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1695,6 +1695,13 @@ package android.app.smartspace { package android.app.time { + public final class Capabilities { + field public static final int CAPABILITY_NOT_ALLOWED = 20; // 0x14 + field public static final int CAPABILITY_NOT_APPLICABLE = 30; // 0x1e + field public static final int CAPABILITY_NOT_SUPPORTED = 10; // 0xa + field public static final int CAPABILITY_POSSESSED = 40; // 0x28 + } + public final class TimeManager { method @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public void addTimeZoneDetectorListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.time.TimeManager.TimeZoneDetectorListener); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) public android.app.time.TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig(); @@ -1711,10 +1718,6 @@ package android.app.time { method public int getConfigureAutoDetectionEnabledCapability(); method public int getConfigureGeoDetectionEnabledCapability(); method public void writeToParcel(@NonNull android.os.Parcel, int); - field public static final int CAPABILITY_NOT_ALLOWED = 20; // 0x14 - field public static final int CAPABILITY_NOT_APPLICABLE = 30; // 0x1e - field public static final int CAPABILITY_NOT_SUPPORTED = 10; // 0xa - field public static final int CAPABILITY_POSSESSED = 40; // 0x28 field @NonNull public static final android.os.Parcelable.Creator<android.app.time.TimeZoneCapabilities> CREATOR; } diff --git a/core/java/android/app/time/Capabilities.java b/core/java/android/app/time/Capabilities.java new file mode 100644 index 000000000000..33db7211f3b4 --- /dev/null +++ b/core/java/android/app/time/Capabilities.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2021 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.IntDef; +import android.annotation.SystemApi; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * A capability is the ability for the user to configure something or perform an action. This + * information is exposed so that system apps like SettingsUI can be dynamic, rather than + * hard-coding knowledge of when configuration or actions are applicable / available to the user. + * + * <p>Capabilities have states that users cannot change directly. They may influence some + * capabilities indirectly by agreeing to certain device-wide behaviors such as location sharing, or + * by changing the configuration. See the {@code CAPABILITY_} constants for details. + * + * <p>Actions have associated methods, see the documentation for each action for details. + * + * <p>Note: Capabilities are independent of app permissions required to call the associated APIs. + * + * @hide + */ +@SystemApi +public final class Capabilities { + + /** @hide */ + @IntDef({ CAPABILITY_NOT_SUPPORTED, CAPABILITY_NOT_ALLOWED, CAPABILITY_NOT_APPLICABLE, + CAPABILITY_POSSESSED }) + @Retention(RetentionPolicy.SOURCE) + public @interface CapabilityState {} + + /** + * Indicates that a capability is not supported on this device, e.g. because of form factor or + * hardware. The associated UI should usually not be shown to the user. + */ + public static final int CAPABILITY_NOT_SUPPORTED = 10; + + /** + * Indicates that a capability is supported on this device, but not allowed for the user, e.g. + * if the capability relates to the ability to modify settings the user is not able to. + * This could be because of the user's type (e.g. maybe it applies to the primary user only) or + * device policy. Depending on the capability, this could mean the associated UI + * should be hidden, or displayed but disabled. + */ + public static final int CAPABILITY_NOT_ALLOWED = 20; + + /** + * Indicates that a capability is possessed but not currently applicable, e.g. if the + * capability relates to the ability to modify settings, the user has the ability to modify + * it, but it is currently rendered irrelevant by other settings or other device state (flags, + * resource config, etc.). The associated UI may be hidden, disabled, or left visible (but + * ineffective) depending on requirements. + */ + public static final int CAPABILITY_NOT_APPLICABLE = 30; + + /** Indicates that a capability is possessed by the user. */ + public static final int CAPABILITY_POSSESSED = 40; + + private Capabilities() {} + +} diff --git a/core/java/android/app/time/TimeCapabilities.aidl b/core/java/android/app/time/TimeCapabilities.aidl new file mode 100644 index 000000000000..f44b791e6ce7 --- /dev/null +++ b/core/java/android/app/time/TimeCapabilities.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 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 TimeCapabilities; diff --git a/core/java/android/app/time/TimeCapabilities.java b/core/java/android/app/time/TimeCapabilities.java new file mode 100644 index 000000000000..fff36c4a7096 --- /dev/null +++ b/core/java/android/app/time/TimeCapabilities.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2021 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.app.time.Capabilities.CapabilityState; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.UserHandle; + +import java.util.Objects; + +/** + * Time-relate capabilities for a user. + * + * <p>For configuration settings capabilities, the associated settings value can be found via + * {@link TimeManager#getTimeCapabilitiesAndConfig()} and may be changed using {@link + * TimeManager#updateTimeConfiguration(TimeConfiguration)} (if the user's capabilities + * allow). + * + * @hide + */ +public final class TimeCapabilities implements Parcelable { + + public static final @NonNull Creator<TimeCapabilities> CREATOR = + new Creator<TimeCapabilities>() { + public TimeCapabilities createFromParcel(Parcel in) { + return TimeCapabilities.createFromParcel(in); + } + + public TimeCapabilities[] newArray(int size) { + return new TimeCapabilities[size]; + } + }; + + + /** + * 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 mConfigureAutoTimeDetectionEnabledCapability; + private final @CapabilityState int mSuggestTimeManuallyCapability; + + private TimeCapabilities(@NonNull Builder builder) { + this.mUserHandle = Objects.requireNonNull(builder.mUserHandle); + this.mConfigureAutoTimeDetectionEnabledCapability = + builder.mConfigureAutoDetectionEnabledCapability; + this.mSuggestTimeManuallyCapability = + builder.mSuggestTimeManuallyCapability; + } + + @NonNull + private static TimeCapabilities createFromParcel(Parcel in) { + UserHandle userHandle = UserHandle.readFromParcel(in); + return new TimeCapabilities.Builder(userHandle) + .setConfigureAutoTimeDetectionEnabledCapability(in.readInt()) + .setSuggestTimeManuallyCapability(in.readInt()) + .build(); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + UserHandle.writeToParcel(mUserHandle, dest); + dest.writeInt(mConfigureAutoTimeDetectionEnabledCapability); + dest.writeInt(mSuggestTimeManuallyCapability); + } + + /** + * Returns the capability state associated with the user's ability to modify the automatic time + * detection setting. + */ + @CapabilityState + public int getConfigureAutoTimeDetectionEnabledCapability() { + return mConfigureAutoTimeDetectionEnabledCapability; + } + + /** + * Returns the capability state associated with the user's ability to manually set time on a + * device. + */ + @CapabilityState + public int getSuggestTimeManuallyCapability() { + return mSuggestTimeManuallyCapability; + } + + @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; + TimeCapabilities that = (TimeCapabilities) o; + return mConfigureAutoTimeDetectionEnabledCapability + == that.mConfigureAutoTimeDetectionEnabledCapability + && mSuggestTimeManuallyCapability == that.mSuggestTimeManuallyCapability + && mUserHandle.equals(that.mUserHandle); + } + + @Override + public int hashCode() { + return Objects.hash(mUserHandle, mConfigureAutoTimeDetectionEnabledCapability, + mSuggestTimeManuallyCapability); + } + + @Override + public String toString() { + return "TimeCapabilities{" + + "mUserHandle=" + mUserHandle + + ", mConfigureAutoTimeDetectionEnabledCapability=" + + mConfigureAutoTimeDetectionEnabledCapability + + ", mSuggestTimeManuallyCapability=" + mSuggestTimeManuallyCapability + + '}'; + } + + /** + * A builder of {@link TimeCapabilities} objects. + * + * @hide + */ + public static class Builder { + @NonNull private final UserHandle mUserHandle; + private @CapabilityState int mConfigureAutoDetectionEnabledCapability; + private @CapabilityState int mSuggestTimeManuallyCapability; + + public Builder(@NonNull TimeCapabilities timeCapabilities) { + Objects.requireNonNull(timeCapabilities); + this.mUserHandle = timeCapabilities.mUserHandle; + this.mConfigureAutoDetectionEnabledCapability = + timeCapabilities.mConfigureAutoTimeDetectionEnabledCapability; + this.mSuggestTimeManuallyCapability = + timeCapabilities.mSuggestTimeManuallyCapability; + } + + public Builder(@NonNull UserHandle userHandle) { + this.mUserHandle = Objects.requireNonNull(userHandle); + } + + /** Sets the state for automatic time detection config. */ + public Builder setConfigureAutoTimeDetectionEnabledCapability( + @CapabilityState int setConfigureAutoTimeDetectionEnabledCapability) { + this.mConfigureAutoDetectionEnabledCapability = + setConfigureAutoTimeDetectionEnabledCapability; + return this; + } + + /** Sets the state for manual time change. */ + public Builder setSuggestTimeManuallyCapability( + @CapabilityState int suggestTimeManuallyCapability) { + this.mSuggestTimeManuallyCapability = suggestTimeManuallyCapability; + return this; + } + + /** Returns the {@link TimeCapabilities}. */ + public TimeCapabilities build() { + verifyCapabilitySet(mConfigureAutoDetectionEnabledCapability, + "configureAutoDetectionEnabledCapability"); + verifyCapabilitySet(mSuggestTimeManuallyCapability, "suggestTimeManuallyCapability"); + return new TimeCapabilities(this); + } + + private void verifyCapabilitySet(int value, String name) { + if (value == 0) { + throw new IllegalStateException(name + " was not set"); + } + } + } +} diff --git a/core/java/android/app/time/TimeCapabilitiesAndConfig.aidl b/core/java/android/app/time/TimeCapabilitiesAndConfig.aidl new file mode 100644 index 000000000000..183dcaf6e2e6 --- /dev/null +++ b/core/java/android/app/time/TimeCapabilitiesAndConfig.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 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 TimeCapabilitiesAndConfig;
\ No newline at end of file diff --git a/core/java/android/app/time/TimeCapabilitiesAndConfig.java b/core/java/android/app/time/TimeCapabilitiesAndConfig.java new file mode 100644 index 000000000000..4a1044760064 --- /dev/null +++ b/core/java/android/app/time/TimeCapabilitiesAndConfig.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2021 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 TimeCapabilities} and {@link TimeConfiguration}. + * + * @hide + */ +public final class TimeCapabilitiesAndConfig implements Parcelable { + + public static final @NonNull Creator<TimeCapabilitiesAndConfig> CREATOR = + new Creator<TimeCapabilitiesAndConfig>() { + @Override + public TimeCapabilitiesAndConfig createFromParcel(Parcel source) { + return TimeCapabilitiesAndConfig.readFromParcel(source); + } + + @Override + public TimeCapabilitiesAndConfig[] newArray(int size) { + return new TimeCapabilitiesAndConfig[size]; + } + }; + + @NonNull + private final TimeCapabilities mTimeCapabilities; + + @NonNull + private final TimeConfiguration mTimeConfiguration; + + /** + * @hide + */ + public TimeCapabilitiesAndConfig(@NonNull TimeCapabilities timeCapabilities, + @NonNull TimeConfiguration timeConfiguration) { + mTimeCapabilities = Objects.requireNonNull(timeCapabilities); + mTimeConfiguration = Objects.requireNonNull(timeConfiguration); + } + + @NonNull + private static TimeCapabilitiesAndConfig readFromParcel(Parcel in) { + TimeCapabilities capabilities = in.readParcelable(null); + TimeConfiguration configuration = in.readParcelable(null); + return new TimeCapabilitiesAndConfig(capabilities, configuration); + } + + /** + * Returns the user's time behaviour capabilities. + * + * @hide + */ + @NonNull + public TimeCapabilities getTimeCapabilities() { + return mTimeCapabilities; + } + + /** + * Returns the user's time behaviour configuration. + * + * @hide + */ + @NonNull + public TimeConfiguration getTimeConfiguration() { + return mTimeConfiguration; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelable(mTimeCapabilities, flags); + dest.writeParcelable(mTimeConfiguration, flags); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TimeCapabilitiesAndConfig that = (TimeCapabilitiesAndConfig) o; + return mTimeCapabilities.equals(that.mTimeCapabilities) + && mTimeConfiguration.equals(that.mTimeConfiguration); + } + + @Override + public int hashCode() { + return Objects.hash(mTimeCapabilities, mTimeConfiguration); + } + + @Override + public String toString() { + return "TimeCapabilitiesAndConfig{" + + "mTimeCapabilities=" + mTimeCapabilities + + ", mTimeConfiguration=" + mTimeConfiguration + + '}'; + } +} diff --git a/core/java/android/app/time/TimeConfiguration.aidl b/core/java/android/app/time/TimeConfiguration.aidl new file mode 100644 index 000000000000..eb5bfd6d2272 --- /dev/null +++ b/core/java/android/app/time/TimeConfiguration.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 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 TimeConfiguration; diff --git a/core/java/android/app/time/TimeConfiguration.java b/core/java/android/app/time/TimeConfiguration.java new file mode 100644 index 000000000000..70aede034d27 --- /dev/null +++ b/core/java/android/app/time/TimeConfiguration.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2021 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.StringDef; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * User visible settings that control the behavior of the time zone detector / manual time zone + * entry. + * + * @hide + */ +public final class TimeConfiguration implements Parcelable { + + public static final @NonNull Creator<TimeConfiguration> CREATOR = + new Creator<TimeConfiguration>() { + @Override + public TimeConfiguration createFromParcel(Parcel source) { + return TimeConfiguration.readFromParcel(source); + } + + @Override + public TimeConfiguration[] newArray(int size) { + return new TimeConfiguration[size]; + } + }; + + @StringDef(SETTING_AUTO_DETECTION_ENABLED) + @Retention(RetentionPolicy.SOURCE) + @interface Setting {} + + @Setting + private static final String SETTING_AUTO_DETECTION_ENABLED = "autoDetectionEnabled"; + + @NonNull + private final Bundle mBundle; + + private TimeConfiguration(Builder builder) { + this.mBundle = builder.mBundle; + } + + /** + * Returns the value of the {@link #SETTING_AUTO_DETECTION_ENABLED} setting. This + * controls whether a device will attempt to determine the time automatically using + * contextual information if the device supports auto detection. + */ + public boolean isAutoDetectionEnabled() { + return mBundle.getBoolean(SETTING_AUTO_DETECTION_ENABLED); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeBundle(mBundle); + } + + private static TimeConfiguration readFromParcel(Parcel in) { + return new TimeConfiguration.Builder() + .merge(in.readBundle()) + .build(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TimeConfiguration that = (TimeConfiguration) o; + return mBundle.kindofEquals(that.mBundle); + } + + @Override + public int hashCode() { + return Objects.hash(mBundle); + } + + @Override + public String toString() { + return "TimeConfiguration{" + + "mBundle=" + mBundle + + '}'; + } + + /** + * A builder for {@link TimeConfiguration} objects. + * + * @hide + */ + public static final class Builder { + private final Bundle mBundle = new Bundle(); + + public Builder() {} + + public Builder(@NonNull TimeConfiguration configuration) { + mBundle.putAll(configuration.mBundle); + } + + /** Sets whether auto detection is enabled or not. */ + @NonNull + public Builder setAutoDetectionEnabled(boolean enabled) { + mBundle.putBoolean(SETTING_AUTO_DETECTION_ENABLED, enabled); + return this; + } + + Builder merge(@NonNull Bundle bundle) { + mBundle.putAll(bundle); + return this; + } + + /** Returns {@link TimeConfiguration} object. */ + @NonNull + public TimeConfiguration build() { + return new TimeConfiguration(this); + } + } +} diff --git a/core/java/android/app/time/TimeManager.java b/core/java/android/app/time/TimeManager.java index 430960fb11a8..c8fa5c8f28e2 100644 --- a/core/java/android/app/time/TimeManager.java +++ b/core/java/android/app/time/TimeManager.java @@ -75,7 +75,7 @@ public final class TimeManager { @NonNull public TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig() { if (DEBUG) { - Log.d(TAG, "getTimeZoneCapabilities called"); + Log.d(TAG, "getTimeZoneCapabilitiesAndConfig called"); } try { return mITimeZoneDetectorService.getCapabilitiesAndConfig(); @@ -85,6 +85,44 @@ public final class TimeManager { } /** + * Returns the calling user's time capabilities and configuration. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) + @NonNull + public TimeCapabilitiesAndConfig getTimeCapabilitiesAndConfig() { + if (DEBUG) { + Log.d(TAG, "getTimeCapabilitiesAndConfig called"); + } + try { + return mITimeDetectorService.getCapabilitiesAndConfig(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Modifies the time detection configuration. + * + * @return {@code true} if all the configuration settings specified have been set to the + * new values, {@code false} if none have + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) + public boolean updateTimeConfiguration(@NonNull TimeConfiguration configuration) { + if (DEBUG) { + Log.d(TAG, "updateTimeConfiguration called: " + configuration); + } + try { + return mITimeDetectorService.updateConfiguration(configuration); + } 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 @@ -97,11 +135,11 @@ public final class TimeManager { * 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} + * Capabilities#CAPABILITY_NOT_SUPPORTED} or {@link + * Capabilities#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 + * Capabilities#CAPABILITY_NOT_APPLICABLE} or {@link + * Capabilities#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 diff --git a/core/java/android/app/time/TimeZoneCapabilities.java b/core/java/android/app/time/TimeZoneCapabilities.java index a27be96e6e69..433b4200eece 100644 --- a/core/java/android/app/time/TimeZoneCapabilities.java +++ b/core/java/android/app/time/TimeZoneCapabilities.java @@ -16,77 +16,33 @@ package android.app.time; -import android.annotation.IntDef; +import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.app.time.Capabilities.CapabilityState; 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; import java.util.Objects; /** - * Time zone-related capabilities for a user. A capability is the ability for the user to configure - * something or perform an action. This information is exposed so that system apps like SettingsUI - * can be dynamic, rather than hard-coding knowledge of when configuration or actions are applicable - * / available to the user. - * - * <p>Capabilities have states that users cannot change directly. They may influence some - * capabilities indirectly by agreeing to certain device-wide behaviors such as location sharing, or - * by changing the configuration. See the {@code CAPABILITY_} constants for details. - * - * <p>Actions have associated methods, see the documentation for each action for details. + * Time zone-related capabilities for a user. * * <p>For configuration settings capabilities, the associated settings value can be found via * {@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) - public @interface CapabilityState {} - - /** - * Indicates that a capability is not supported on this device, e.g. because of form factor or - * hardware. The associated UI should usually not be shown to the user. - */ - public static final int CAPABILITY_NOT_SUPPORTED = 10; - - /** - * Indicates that a capability is supported on this device, but not allowed for the user, e.g. - * if the capability relates to the ability to modify settings the user is not able to. - * This could be because of the user's type (e.g. maybe it applies to the primary user only) or - * device policy. Depending on the capability, this could mean the associated UI - * should be hidden, or displayed but disabled. - */ - public static final int CAPABILITY_NOT_ALLOWED = 20; - - /** - * Indicates that a capability is possessed but not currently applicable, e.g. if the - * capability relates to the ability to modify settings, the user has the ability to modify - * it, but it is currently rendered irrelevant by other settings or other device state (flags, - * resource config, etc.). The associated UI may be hidden, disabled, or left visible (but - * ineffective) depending on requirements. - */ - public static final int CAPABILITY_NOT_APPLICABLE = 30; - - /** Indicates that a capability is possessed by the user. */ - public static final int CAPABILITY_POSSESSED = 40; - public static final @NonNull Creator<TimeZoneCapabilities> CREATOR = new Creator<TimeZoneCapabilities>() { public TimeZoneCapabilities createFromParcel(Parcel in) { @@ -159,7 +115,8 @@ public final class TimeZoneCapabilities implements Parcelable { * on a device via {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}. * * <p>The suggestion will be ignored in all cases unless the value is {@link - * #CAPABILITY_POSSESSED}. See also {@link TimeZoneConfiguration#isAutoDetectionEnabled()}. + * Capabilities#CAPABILITY_POSSESSED}. See also + * {@link TimeZoneConfiguration#isAutoDetectionEnabled()}. * * @hide */ diff --git a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java index f9a0c74312fc..a9ea76f77958 100644 --- a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java +++ b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java @@ -113,7 +113,7 @@ public final class TimeZoneCapabilitiesAndConfig implements Parcelable { @Override public String toString() { - return "TimeZoneDetectorCapabilitiesAndConfig{" + return "TimeZoneCapabilitiesAndConfig{" + "mCapabilities=" + mCapabilities + ", mConfiguration=" + mConfiguration + '}'; diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl index c4546be10601..9a6c33589123 100644 --- a/core/java/android/app/timedetector/ITimeDetectorService.aidl +++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl @@ -17,6 +17,8 @@ package android.app.timedetector; import android.app.time.ExternalTimeSuggestion; +import android.app.time.TimeCapabilitiesAndConfig; +import android.app.time.TimeConfiguration; import android.app.timedetector.GnssTimeSuggestion; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; @@ -36,6 +38,9 @@ import android.app.timedetector.TelephonyTimeSuggestion; * {@hide} */ interface ITimeDetectorService { + TimeCapabilitiesAndConfig getCapabilitiesAndConfig(); + boolean updateConfiguration(in TimeConfiguration timeConfiguration); + void suggestExternalTime( in ExternalTimeSuggestion timeSuggestion); void suggestGnssTime(in GnssTimeSuggestion timeSuggestion); boolean suggestManualTime(in ManualTimeSuggestion timeSuggestion); diff --git a/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java b/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java new file mode 100644 index 000000000000..85073b0ecfdd --- /dev/null +++ b/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2021 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.Capabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; +import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.fail; + +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 TimeCapabilitiesTest { + + private static final UserHandle USER_HANDLE = UserHandle.of(332211); + + @Test + public void testBuilder() { + TimeCapabilities capabilities = new TimeCapabilities.Builder(USER_HANDLE) + .setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_NOT_APPLICABLE) + .setSuggestTimeManuallyCapability(CAPABILITY_NOT_SUPPORTED) + .build(); + + assertThat(capabilities.getConfigureAutoTimeDetectionEnabledCapability()) + .isEqualTo(CAPABILITY_NOT_APPLICABLE); + assertThat(capabilities.getSuggestTimeManuallyCapability()) + .isEqualTo(CAPABILITY_NOT_SUPPORTED); + + try { + new TimeCapabilities.Builder(USER_HANDLE) + .build(); + fail("Should throw IllegalStateException"); + } catch (IllegalStateException ignored) { + // expected + } + + try { + new TimeCapabilities.Builder(USER_HANDLE) + .setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_NOT_APPLICABLE) + .build(); + fail("Should throw IllegalStateException"); + } catch (IllegalStateException ignored) { + // expected + } + + try { + new TimeCapabilities.Builder(USER_HANDLE) + .setSuggestTimeManuallyCapability(CAPABILITY_NOT_APPLICABLE) + .build(); + fail("Should throw IllegalStateException"); + } catch (IllegalStateException ignored) { + // expected + } + } + + @Test + public void userHandle_notIgnoredInEquals() { + TimeCapabilities firstUserCapabilities = new TimeCapabilities.Builder(UserHandle.of(1)) + .setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_POSSESSED) + .setSuggestTimeManuallyCapability(CAPABILITY_POSSESSED) + .build(); + + TimeCapabilities secondUserCapabilities = new TimeCapabilities.Builder(UserHandle.of(2)) + .setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_POSSESSED) + .setSuggestTimeManuallyCapability(CAPABILITY_POSSESSED) + .build(); + + assertThat(firstUserCapabilities).isNotEqualTo(secondUserCapabilities); + } + + @Test + public void testParcelable() { + TimeCapabilities.Builder builder = new TimeCapabilities.Builder(USER_HANDLE) + .setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_NOT_SUPPORTED) + .setSuggestTimeManuallyCapability(CAPABILITY_NOT_SUPPORTED); + + assertRoundTripParcelable(builder.build()); + + builder.setSuggestTimeManuallyCapability(CAPABILITY_POSSESSED); + assertRoundTripParcelable(builder.build()); + + builder.setConfigureAutoTimeDetectionEnabledCapability(CAPABILITY_POSSESSED); + assertRoundTripParcelable(builder.build()); + } + +} diff --git a/core/tests/coretests/src/android/app/time/TimeConfigurationTest.java b/core/tests/coretests/src/android/app/time/TimeConfigurationTest.java new file mode 100644 index 000000000000..7c7cd12bcb73 --- /dev/null +++ b/core/tests/coretests/src/android/app/time/TimeConfigurationTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2021 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.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; + +import static com.google.common.truth.Truth.assertThat; + +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 TimeConfigurationTest { + + @Test + public void testBuilder() { + TimeConfiguration first = new TimeConfiguration.Builder() + .setAutoDetectionEnabled(true) + .build(); + + assertThat(first.isAutoDetectionEnabled()).isTrue(); + + TimeConfiguration copyFromBuilderConfiguration = new TimeConfiguration.Builder(first) + .build(); + + assertThat(first).isEqualTo(copyFromBuilderConfiguration); + } + + @Test + public void testParcelable() { + TimeConfiguration.Builder builder = new TimeConfiguration.Builder(); + + assertRoundTripParcelable(builder.setAutoDetectionEnabled(true).build()); + + assertRoundTripParcelable(builder.setAutoDetectionEnabled(false).build()); + } + +} diff --git a/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java b/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java index 01a25b27baf6..dd93997b00c5 100644 --- a/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java +++ b/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java @@ -16,8 +16,8 @@ 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.time.Capabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; import static org.junit.Assert.assertEquals; diff --git a/services/core/java/com/android/server/timedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java new file mode 100644 index 000000000000..f00f856c2910 --- /dev/null +++ b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.timedetector; + +import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; + +import android.annotation.UserIdInt; +import android.app.time.Capabilities.CapabilityState; +import android.app.time.TimeCapabilities; +import android.app.time.TimeCapabilitiesAndConfig; +import android.app.time.TimeConfiguration; +import android.os.UserHandle; + +import java.util.Objects; + +/** + * Holds configuration values that affect time behaviour. + */ +public final class ConfigurationInternal { + + private final @UserIdInt int mUserId; + private final boolean mUserConfigAllowed; + private final boolean mAutoDetectionEnabled; + + private ConfigurationInternal(Builder builder) { + mUserId = builder.mUserId; + mUserConfigAllowed = builder.mUserConfigAllowed; + mAutoDetectionEnabled = builder.mAutoDetectionEnabled; + } + + /** Returns a {@link TimeCapabilitiesAndConfig} objects based on configuration values. */ + public TimeCapabilitiesAndConfig capabilitiesAndConfig() { + return new TimeCapabilitiesAndConfig(timeCapabilities(), timeConfiguration()); + } + + private TimeConfiguration timeConfiguration() { + return new TimeConfiguration.Builder() + .setAutoDetectionEnabled(mAutoDetectionEnabled) + .build(); + } + + private TimeCapabilities timeCapabilities() { + @CapabilityState int configureAutoTimeDetectionEnabledCapability = + mUserConfigAllowed + ? CAPABILITY_POSSESSED + : CAPABILITY_NOT_ALLOWED; + + @CapabilityState int suggestTimeManuallyCapability = + mUserConfigAllowed + ? CAPABILITY_POSSESSED + : CAPABILITY_NOT_ALLOWED; + + return new TimeCapabilities.Builder(UserHandle.of(mUserId)) + .setConfigureAutoTimeDetectionEnabledCapability( + configureAutoTimeDetectionEnabledCapability) + .setSuggestTimeManuallyCapability(suggestTimeManuallyCapability) + .build(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ConfigurationInternal that = (ConfigurationInternal) o; + return mUserId == that.mUserId + && mUserConfigAllowed == that.mUserConfigAllowed + && mAutoDetectionEnabled == that.mAutoDetectionEnabled; + } + + @Override + public int hashCode() { + return Objects.hash(mUserId, mUserConfigAllowed, mAutoDetectionEnabled); + } + + @Override + public String toString() { + return "ConfigurationInternal{" + + "mUserId=" + mUserId + + ", mUserConfigAllowed=" + mUserConfigAllowed + + ", mAutoDetectionEnabled=" + mAutoDetectionEnabled + + '}'; + } + + static final class Builder { + private final @UserIdInt int mUserId; + private boolean mUserConfigAllowed; + private boolean mAutoDetectionEnabled; + + Builder(@UserIdInt int userId) { + mUserId = userId; + } + + Builder setUserConfigAllowed(boolean userConfigAllowed) { + mUserConfigAllowed = userConfigAllowed; + return this; + } + + Builder setAutoDetectionEnabled(boolean autoDetectionEnabled) { + mAutoDetectionEnabled = autoDetectionEnabled; + return this; + } + + ConfigurationInternal build() { + return new ConfigurationInternal(this); + } + } + +} diff --git a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java index 5cd171839996..4f5e8fa9c944 100644 --- a/services/core/java/com/android/server/timedetector/EnvironmentImpl.java +++ b/services/core/java/com/android/server/timedetector/EnvironmentImpl.java @@ -21,6 +21,7 @@ import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPH import static com.android.server.timedetector.TimeDetectorStrategy.stringToOrigin; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; @@ -28,6 +29,8 @@ import android.os.Build; import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.util.Slog; @@ -71,6 +74,7 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme @NonNull private final ContentResolver mContentResolver; @NonNull private final PowerManager.WakeLock mWakeLock; @NonNull private final AlarmManager mAlarmManager; + @NonNull private final UserManager mUserManager; @NonNull private final int[] mOriginPriorities; public EnvironmentImpl(@NonNull Context context) { @@ -83,6 +87,8 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme mAlarmManager = Objects.requireNonNull(context.getSystemService(AlarmManager.class)); + mUserManager = Objects.requireNonNull(context.getSystemService(UserManager.class)); + mSystemClockUpdateThresholdMillis = SystemProperties.getInt("ro.sys.time_detector_update_diff", SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT); @@ -115,6 +121,14 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme } @Override + public ConfigurationInternal configurationInternal(@UserIdInt int userId) { + return new ConfigurationInternal.Builder(userId) + .setUserConfigAllowed(isUserConfigAllowed(userId)) + .setAutoDetectionEnabled(isAutoTimeDetectionEnabled()) + .build(); + } + + @Override public void acquireWakeLock() { if (mWakeLock.isHeld()) { Slog.wtf(TAG, "WakeLock " + mWakeLock + " already held"); @@ -150,6 +164,11 @@ public final class EnvironmentImpl implements TimeDetectorStrategyImpl.Environme } } + private boolean isUserConfigAllowed(@UserIdInt int userId) { + UserHandle userHandle = UserHandle.of(userId); + return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle); + } + private static int[] getOriginPriorities(@NonNull Context context) { String[] originStrings = context.getResources().getStringArray(R.array.config_autoTimeSourcesPriority); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index b210339adf79..eefa045abe1b 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java @@ -18,7 +18,10 @@ package com.android.server.timedetector; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.time.ExternalTimeSuggestion; +import android.app.time.TimeCapabilitiesAndConfig; +import android.app.time.TimeConfiguration; import android.app.timedetector.GnssTimeSuggestion; import android.app.timedetector.ITimeDetectorService; import android.app.timedetector.ManualTimeSuggestion; @@ -36,6 +39,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.server.FgThread; import com.android.server.SystemService; +import com.android.server.timezonedetector.CallerIdentityInjector; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -71,6 +75,7 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @NonNull private final Handler mHandler; @NonNull private final Context mContext; @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy; + @NonNull private final CallerIdentityInjector mCallerIdentityInjector; private static TimeDetectorService create(@NonNull Context context) { TimeDetectorStrategyImpl.Environment environment = new EnvironmentImpl(context); @@ -97,9 +102,42 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { @VisibleForTesting public TimeDetectorService(@NonNull Context context, @NonNull Handler handler, @NonNull TimeDetectorStrategy timeDetectorStrategy) { + this(context, handler, timeDetectorStrategy, CallerIdentityInjector.REAL); + } + + @VisibleForTesting + public TimeDetectorService(@NonNull Context context, @NonNull Handler handler, + @NonNull TimeDetectorStrategy timeDetectorStrategy, + @NonNull CallerIdentityInjector callerIdentityInjector) { mContext = Objects.requireNonNull(context); mHandler = Objects.requireNonNull(handler); mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy); + mCallerIdentityInjector = Objects.requireNonNull(callerIdentityInjector); + } + + @Override + public TimeCapabilitiesAndConfig getCapabilitiesAndConfig() { + int userId = mCallerIdentityInjector.getCallingUserId(); + return getTimeCapabilitiesAndConfig(userId); + } + + private TimeCapabilitiesAndConfig getTimeCapabilitiesAndConfig(@UserIdInt int userId) { + enforceManageTimeDetectorPermission(); + + final long token = mCallerIdentityInjector.clearCallingIdentity(); + try { + ConfigurationInternal configurationInternal = + mTimeDetectorStrategy.getConfigurationInternal(userId); + return configurationInternal.capabilitiesAndConfig(); + } finally { + mCallerIdentityInjector.restoreCallingIdentity(token); + } + } + + @Override + public boolean updateConfiguration(TimeConfiguration timeConfiguration) { + // TODO(b/172891783) Add actual logic + return false; } @Override @@ -193,4 +231,11 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { android.Manifest.permission.SET_TIME, "suggest time from external source"); } + + private void enforceManageTimeDetectorPermission() { + mContext.enforceCallingPermission( + android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION, + "manage time and time zone detection"); + } + } diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index 792f372b0c49..cde66becdee2 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -18,6 +18,7 @@ package com.android.server.timedetector; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.app.time.ExternalTimeSuggestion; import android.app.timedetector.GnssTimeSuggestion; import android.app.timedetector.ManualTimeSuggestion; @@ -88,6 +89,9 @@ public interface TimeDetectorStrategy extends Dumpable { /** Processes the suggested time from external sources. */ void suggestExternalTime(@NonNull ExternalTimeSuggestion timeSuggestion); + /** Returns the configuration that controls time detector behaviour for specified user. */ + ConfigurationInternal getConfigurationInternal(@UserIdInt int userId); + /** * Handles the auto-time configuration changing For example, when the auto-time setting is * toggled on or off. diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index 16e8632c6e40..289d8d6e648e 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -22,6 +22,7 @@ import static java.util.stream.Collectors.joining; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.AlarmManager; import android.app.time.ExternalTimeSuggestion; import android.app.timedetector.GnssTimeSuggestion; @@ -155,6 +156,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { */ @Origin int[] autoOriginPriorities(); + /** + * Returns {@link ConfigurationInternal} for specified user. + */ + @NonNull + ConfigurationInternal configurationInternal(@UserIdInt int userId); + /** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */ void acquireWakeLock(); @@ -267,6 +274,12 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } @Override + @NonNull + public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) { + return mEnvironment.configurationInternal(userId); + } + + @Override public synchronized void handleAutoTimeConfigChanged() { boolean enabled = mEnvironment.isAutoTimeDetectionEnabled(); // When automatic time detection is enabled we update the system clock instantly if we can. diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java index 3ae9d641e81c..5ede595f5ed8 100644 --- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java +++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java @@ -16,10 +16,10 @@ package com.android.server.timezonedetector; -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 android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; import android.annotation.NonNull; import android.annotation.UserIdInt; diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java index d163a0e22320..a231dfc4814a 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java @@ -15,7 +15,7 @@ */ package com.android.server.timezonedetector; -import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED; +import static android.app.time.Capabilities.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; diff --git a/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java new file mode 100644 index 000000000000..d4222e6e0b63 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.timedetector; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.time.Capabilities; +import android.app.time.TimeCapabilities; +import android.app.time.TimeCapabilitiesAndConfig; +import android.app.time.TimeConfiguration; +import android.os.UserHandle; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class ConfigurationInternalTest { + + @Test + public void capabilitiesAndConfig() { + int userId = 112233; + ConfigurationInternal configurationInternal = new ConfigurationInternal.Builder(userId) + .setAutoDetectionEnabled(true) + .setUserConfigAllowed(true) + .build(); + + TimeCapabilities timeCapabilities = new TimeCapabilities.Builder(UserHandle.of(userId)) + .setConfigureAutoTimeDetectionEnabledCapability(Capabilities.CAPABILITY_POSSESSED) + .setSuggestTimeManuallyCapability(Capabilities.CAPABILITY_POSSESSED) + .build(); + TimeConfiguration timeConfiguration = new TimeConfiguration.Builder() + .setAutoDetectionEnabled(true) + .build(); + TimeCapabilitiesAndConfig expected = + new TimeCapabilitiesAndConfig(timeCapabilities, timeConfiguration); + + assertThat(configurationInternal.capabilitiesAndConfig()).isEqualTo(expected); + } + +} diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index bbf11fd557a3..106827078290 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -328,6 +328,11 @@ public class TimeDetectorServiceTest { } @Override + public ConfigurationInternal getConfigurationInternal(int userId) { + throw new UnsupportedOperationException(); + } + + @Override public void handleAutoTimeConfigChanged() { mHandleAutoTimeDetectionChangedCalled = true; } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index f7a498bc9d73..095703e6b939 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -1158,6 +1158,11 @@ public class TimeDetectorStrategyImplTest { } @Override + public ConfigurationInternal configurationInternal(int userId) { + throw new UnsupportedOperationException(); + } + + @Override public void acquireWakeLock() { if (mWakeLockAcquired) { fail("Wake lock already acquired"); 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 5d2755221288..aec7a3bc2522 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java @@ -16,10 +16,10 @@ package com.android.server.timezonedetector; -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 android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED; +import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE; +import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED; +import static android.app.time.Capabilities.CAPABILITY_POSSESSED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; |