summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/system-current.txt11
-rw-r--r--core/java/android/app/time/Capabilities.java78
-rw-r--r--core/java/android/app/time/TimeCapabilities.aidl19
-rw-r--r--core/java/android/app/time/TimeCapabilities.java186
-rw-r--r--core/java/android/app/time/TimeCapabilitiesAndConfig.aidl19
-rw-r--r--core/java/android/app/time/TimeCapabilitiesAndConfig.java119
-rw-r--r--core/java/android/app/time/TimeConfiguration.aidl19
-rw-r--r--core/java/android/app/time/TimeConfiguration.java141
-rw-r--r--core/java/android/app/time/TimeManager.java48
-rw-r--r--core/java/android/app/time/TimeZoneCapabilities.java55
-rw-r--r--core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java2
-rw-r--r--core/java/android/app/timedetector/ITimeDetectorService.aidl5
-rw-r--r--core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java112
-rw-r--r--core/tests/coretests/src/android/app/time/TimeConfigurationTest.java56
-rw-r--r--core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java4
-rw-r--r--services/core/java/com/android/server/timedetector/ConfigurationInternal.java123
-rw-r--r--services/core/java/com/android/server/timedetector/EnvironmentImpl.java19
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorService.java45
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java4
-rw-r--r--services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java13
-rw-r--r--services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java8
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/ConfigurationInternalTest.java56
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java8
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;