diff --git a/core/java/android/app/time/TimeCapabilities.java b/core/java/android/app/time/TimeCapabilities.java
index 44bc1788fa6ad..76bad58e924ba 100644
--- a/core/java/android/app/time/TimeCapabilities.java
+++ b/core/java/android/app/time/TimeCapabilities.java
@@ -57,21 +57,21 @@ public final class TimeCapabilities implements Parcelable {
@NonNull
private final UserHandle mUserHandle;
private final @CapabilityState int mConfigureAutoDetectionEnabledCapability;
- private final @CapabilityState int mSuggestManualTimeCapability;
+ private final @CapabilityState int mSetManualTimeCapability;
private TimeCapabilities(@NonNull Builder builder) {
this.mUserHandle = Objects.requireNonNull(builder.mUserHandle);
this.mConfigureAutoDetectionEnabledCapability =
builder.mConfigureAutoDetectionEnabledCapability;
- this.mSuggestManualTimeCapability = builder.mSuggestManualTimeCapability;
+ this.mSetManualTimeCapability = builder.mSetManualTimeCapability;
}
@NonNull
- private static TimeCapabilities createFromParcel(Parcel in) {
+ private static TimeCapabilities createFromParcel(@NonNull Parcel in) {
UserHandle userHandle = UserHandle.readFromParcel(in);
return new TimeCapabilities.Builder(userHandle)
.setConfigureAutoDetectionEnabledCapability(in.readInt())
- .setSuggestManualTimeCapability(in.readInt())
+ .setSetManualTimeCapability(in.readInt())
.build();
}
@@ -79,7 +79,7 @@ public final class TimeCapabilities implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
UserHandle.writeToParcel(mUserHandle, dest);
dest.writeInt(mConfigureAutoDetectionEnabledCapability);
- dest.writeInt(mSuggestManualTimeCapability);
+ dest.writeInt(mSetManualTimeCapability);
}
/**
@@ -94,11 +94,12 @@ public final class TimeCapabilities implements Parcelable {
/**
* Returns the capability state associated with the user's ability to manually set time on a
- * device.
+ * device. The setting can be updated via {@link
+ * TimeManager#updateTimeConfiguration(TimeConfiguration)}.
*/
@CapabilityState
- public int getSuggestManualTimeCapability() {
- return mSuggestManualTimeCapability;
+ public int getSetManualTimeCapability() {
+ return mSetManualTimeCapability;
}
/**
@@ -136,14 +137,14 @@ public final class TimeCapabilities implements Parcelable {
TimeCapabilities that = (TimeCapabilities) o;
return mConfigureAutoDetectionEnabledCapability
== that.mConfigureAutoDetectionEnabledCapability
- && mSuggestManualTimeCapability == that.mSuggestManualTimeCapability
+ && mSetManualTimeCapability == that.mSetManualTimeCapability
&& mUserHandle.equals(that.mUserHandle);
}
@Override
public int hashCode() {
return Objects.hash(mUserHandle, mConfigureAutoDetectionEnabledCapability,
- mSuggestManualTimeCapability);
+ mSetManualTimeCapability);
}
@Override
@@ -152,7 +153,7 @@ public final class TimeCapabilities implements Parcelable {
+ "mUserHandle=" + mUserHandle
+ ", mConfigureAutoDetectionEnabledCapability="
+ mConfigureAutoDetectionEnabledCapability
- + ", mSuggestManualTimeCapability=" + mSuggestManualTimeCapability
+ + ", mSetManualTimeCapability=" + mSetManualTimeCapability
+ '}';
}
@@ -165,7 +166,7 @@ public final class TimeCapabilities implements Parcelable {
@NonNull private final UserHandle mUserHandle;
private @CapabilityState int mConfigureAutoDetectionEnabledCapability;
- private @CapabilityState int mSuggestManualTimeCapability;
+ private @CapabilityState int mSetManualTimeCapability;
public Builder(@NonNull UserHandle userHandle) {
this.mUserHandle = Objects.requireNonNull(userHandle);
@@ -176,18 +177,18 @@ public final class TimeCapabilities implements Parcelable {
this.mUserHandle = timeCapabilities.mUserHandle;
this.mConfigureAutoDetectionEnabledCapability =
timeCapabilities.mConfigureAutoDetectionEnabledCapability;
- this.mSuggestManualTimeCapability = timeCapabilities.mSuggestManualTimeCapability;
+ this.mSetManualTimeCapability = timeCapabilities.mSetManualTimeCapability;
}
- /** Sets the state for automatic time detection config. */
+ /** Sets the value for the "configure automatic time detection" capability. */
public Builder setConfigureAutoDetectionEnabledCapability(@CapabilityState int value) {
this.mConfigureAutoDetectionEnabledCapability = value;
return this;
}
- /** Sets the state for manual time change. */
- public Builder setSuggestManualTimeCapability(@CapabilityState int value) {
- this.mSuggestManualTimeCapability = value;
+ /** Sets the value for the "set manual time" capability. */
+ public Builder setSetManualTimeCapability(@CapabilityState int value) {
+ this.mSetManualTimeCapability = value;
return this;
}
@@ -195,7 +196,7 @@ public final class TimeCapabilities implements Parcelable {
public TimeCapabilities build() {
verifyCapabilitySet(mConfigureAutoDetectionEnabledCapability,
"configureAutoDetectionEnabledCapability");
- verifyCapabilitySet(mSuggestManualTimeCapability, "mSuggestManualTimeCapability");
+ verifyCapabilitySet(mSetManualTimeCapability, "mSetManualTimeCapability");
return new TimeCapabilities(this);
}
diff --git a/core/java/android/app/time/TimeCapabilitiesAndConfig.java b/core/java/android/app/time/TimeCapabilitiesAndConfig.java
index be4d01048cee8..b6a0818257572 100644
--- a/core/java/android/app/time/TimeCapabilitiesAndConfig.java
+++ b/core/java/android/app/time/TimeCapabilitiesAndConfig.java
@@ -71,8 +71,6 @@ public final class TimeCapabilitiesAndConfig implements Parcelable {
/**
* Returns the user's time behaviour capabilities.
- *
- * @hide
*/
@NonNull
public TimeCapabilities getCapabilities() {
@@ -81,8 +79,6 @@ public final class TimeCapabilitiesAndConfig implements Parcelable {
/**
* Returns the user's time behaviour configuration.
- *
- * @hide
*/
@NonNull
public TimeConfiguration getConfiguration() {
diff --git a/core/java/android/app/time/TimeConfiguration.java b/core/java/android/app/time/TimeConfiguration.java
index 11f6ed2a9f880..7d986983160ee 100644
--- a/core/java/android/app/time/TimeConfiguration.java
+++ b/core/java/android/app/time/TimeConfiguration.java
@@ -55,10 +55,16 @@ public final class TimeConfiguration implements Parcelable {
}
};
+ /**
+ * All configuration properties
+ *
+ * @hide
+ */
@StringDef(SETTING_AUTO_DETECTION_ENABLED)
@Retention(RetentionPolicy.SOURCE)
@interface Setting {}
+ /** See {@link TimeConfiguration#isAutoDetectionEnabled()} for details. */
@Setting
private static final String SETTING_AUTO_DETECTION_ENABLED = "autoDetectionEnabled";
diff --git a/core/java/android/app/time/TimeManager.java b/core/java/android/app/time/TimeManager.java
index d6acb8cd1076a..6a833fd772afa 100644
--- a/core/java/android/app/time/TimeManager.java
+++ b/core/java/android/app/time/TimeManager.java
@@ -21,11 +21,14 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.timedetector.ITimeDetectorService;
+import android.app.timedetector.ManualTimeSuggestion;
import android.app.timezonedetector.ITimeZoneDetectorService;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.TimestampedValue;
import android.util.ArrayMap;
import android.util.Log;
@@ -274,4 +277,152 @@ public final class TimeManager {
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Returns a snapshot of the device's current system clock time state. See also {@link
+ * #confirmTime(UnixEpochTime)} for how this information can be used.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION)
+ @NonNull
+ public TimeState getTimeState() {
+ if (DEBUG) {
+ Log.d(TAG, "getTimeState called");
+ }
+ try {
+ return mITimeDetectorService.getTimeState();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Confirms the device's current time during device setup, raising the system's confidence in
+ * the time if needed. Unlike {@link #setManualTime(UnixEpochTime)}, which can only be used when
+ * automatic time detection is currently disabled, this method can be used regardless of the
+ * automatic time detection setting, but only to confirm the current time (which may have been
+ * set via automatic means). Use {@link #getTimeState()} to obtain the time state to confirm.
+ *
+ *
Returns {@code false} if the confirmation is invalid, i.e. if the time being
+ * confirmed is no longer the time the device is currently set to. Confirming a time
+ * in which the system already has high confidence will return {@code true}.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION)
+ public boolean confirmTime(@NonNull UnixEpochTime unixEpochTime) {
+ if (DEBUG) {
+ Log.d(TAG, "confirmTime called: " + unixEpochTime);
+ }
+ try {
+ return mITimeDetectorService.confirmTime(unixEpochTime);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Attempts to set the device's time, expected to be determined from the user's manually entered
+ * information.
+ *
+ *
Returns {@code false} if the time is invalid, or the device configuration / user
+ * capabilities prevents the time being accepted, e.g. if the device is currently set to
+ * "automatic time detection". This method returns {@code true} if the time was accepted even
+ * if it is the same as the current device time.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION)
+ public boolean setManualTime(@NonNull UnixEpochTime unixEpochTime) {
+ if (DEBUG) {
+ Log.d(TAG, "setTime called: " + unixEpochTime);
+ }
+ try {
+ TimestampedValue manualTime = new TimestampedValue<>(
+ unixEpochTime.getElapsedRealtimeMillis(),
+ unixEpochTime.getUnixEpochTimeMillis());
+ ManualTimeSuggestion manualTimeSuggestion = new ManualTimeSuggestion(manualTime);
+ manualTimeSuggestion.addDebugInfo("TimeManager.setTime()");
+ manualTimeSuggestion.addDebugInfo("UID: " + android.os.Process.myUid());
+ manualTimeSuggestion.addDebugInfo("UserHandle: " + android.os.Process.myUserHandle());
+ manualTimeSuggestion.addDebugInfo("Process: " + android.os.Process.myProcessName());
+ return mITimeDetectorService.setManualTime(manualTimeSuggestion);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns a snapshot of the device's current time zone state. See also {@link
+ * #confirmTimeZone(String)} and {@link #setManualTimeZone(String)} for how this information may
+ * be used.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION)
+ @NonNull
+ public TimeZoneState getTimeZoneState() {
+ if (DEBUG) {
+ Log.d(TAG, "getTimeZoneState called");
+ }
+ try {
+ return mITimeZoneDetectorService.getTimeZoneState();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Confirms the device's current time zone ID, raising the system's confidence in the time zone
+ * if needed. Unlike {@link #setManualTimeZone(String)}, which can only be used when automatic
+ * time zone detection is currently disabled, this method can be used regardless of the
+ * automatic time zone detection setting, but only to confirm the current value (which may have
+ * been set via automatic means).
+ *
+ *
Returns {@code false} if the confirmation is invalid, i.e. if the time zone ID being
+ * confirmed is no longer the time zone ID the device is currently set to. Confirming a time
+ * zone ID in which the system already has high confidence returns {@code true}.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION)
+ public boolean confirmTimeZone(@NonNull String timeZoneId) {
+ if (DEBUG) {
+ Log.d(TAG, "confirmTimeZone called: " + timeZoneId);
+ }
+ try {
+ return mITimeZoneDetectorService.confirmTimeZone(timeZoneId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Attempts to set the device's time zone, expected to be determined from a user's manually
+ * entered information.
+ *
+ *
Returns {@code false} if the time zone is invalid, or the device configuration / user
+ * capabilities prevents the time zone being accepted, e.g. if the device is currently set to
+ * "automatic time zone detection". {@code true} is returned if the time zone is accepted. A
+ * time zone that is accepted and matches the current device time zone returns {@code true}.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION)
+ public boolean setManualTimeZone(@NonNull String timeZoneId) {
+ if (DEBUG) {
+ Log.d(TAG, "setManualTimeZone called: " + timeZoneId);
+ }
+ try {
+ ManualTimeZoneSuggestion manualTimeZoneSuggestion =
+ new ManualTimeZoneSuggestion(timeZoneId);
+ manualTimeZoneSuggestion.addDebugInfo("TimeManager.setManualTimeZone()");
+ manualTimeZoneSuggestion.addDebugInfo("UID: " + android.os.Process.myUid());
+ manualTimeZoneSuggestion.addDebugInfo("Process: " + android.os.Process.myProcessName());
+ return mITimeZoneDetectorService.setManualTimeZone(manualTimeZoneSuggestion);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/time/TimeState.aidl b/core/java/android/app/time/TimeState.aidl
new file mode 100644
index 0000000000000..70c31d85bd555
--- /dev/null
+++ b/core/java/android/app/time/TimeState.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+parcelable TimeState;
diff --git a/core/java/android/app/time/TimeState.java b/core/java/android/app/time/TimeState.java
new file mode 100644
index 0000000000000..15411e547814c
--- /dev/null
+++ b/core/java/android/app/time/TimeState.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/**
+ * A snapshot of the system time state.
+ *
+ *
{@code mUnixEpochTime} contains a snapshot of the system clock time and elapsed realtime clock
+ * time.
+ *
+ *
{@code mUserShouldConfirmTime} is {@code true} if the system has low confidence in the system
+ * clock time.
+ *
+ * @hide
+ */
+public final class TimeState implements Parcelable {
+
+ public static final @NonNull Creator CREATOR = new Creator<>() {
+ public TimeState createFromParcel(Parcel in) {
+ return TimeState.createFromParcel(in);
+ }
+
+ public TimeState[] newArray(int size) {
+ return new TimeState[size];
+ }
+ };
+
+ @NonNull private final UnixEpochTime mUnixEpochTime;
+ private final boolean mUserShouldConfirmTime;
+
+ /** @hide */
+ public TimeState(@NonNull UnixEpochTime unixEpochTime, boolean userShouldConfirmTime) {
+ mUnixEpochTime = Objects.requireNonNull(unixEpochTime);
+ mUserShouldConfirmTime = userShouldConfirmTime;
+ }
+
+ private static TimeState createFromParcel(Parcel in) {
+ UnixEpochTime unixEpochTime = in.readParcelable(null, UnixEpochTime.class);
+ boolean userShouldConfirmId = in.readBoolean();
+ return new TimeState(unixEpochTime, userShouldConfirmId);
+ }
+
+ /** @hide */
+ @Nullable
+ public static TimeState parseCommandLineArgs(@NonNull ShellCommand cmd) {
+ Long elapsedRealtimeMillis = null;
+ Long unixEpochTimeMillis = null;
+ Boolean userShouldConfirmTime = null;
+ String opt;
+ while ((opt = cmd.getNextArg()) != null) {
+ switch (opt) {
+ case "--elapsed_realtime": {
+ elapsedRealtimeMillis = Long.parseLong(cmd.getNextArgRequired());
+ break;
+ }
+ case "--unix_epoch_time": {
+ unixEpochTimeMillis = Long.parseLong(cmd.getNextArgRequired());
+ break;
+ }
+ case "--user_should_confirm_time": {
+ userShouldConfirmTime = Boolean.parseBoolean(cmd.getNextArgRequired());
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
+ }
+ }
+
+ if (elapsedRealtimeMillis == null) {
+ throw new IllegalArgumentException("No elapsedRealtimeMillis specified.");
+ }
+ if (unixEpochTimeMillis == null) {
+ throw new IllegalArgumentException("No unixEpochTimeMillis specified.");
+ }
+ if (userShouldConfirmTime == null) {
+ throw new IllegalArgumentException("No userShouldConfirmTime specified.");
+ }
+
+ UnixEpochTime unixEpochTime = new UnixEpochTime(elapsedRealtimeMillis, unixEpochTimeMillis);
+ return new TimeState(unixEpochTime, userShouldConfirmTime);
+ }
+
+ /** @hide */
+ public static void printCommandLineOpts(@NonNull PrintWriter pw) {
+ pw.println("TimeState options:");
+ pw.println(" --elapsed_realtime ");
+ pw.println(" --unix_epoch_time ");
+ pw.println(" --user_should_confirm_time {true|false}");
+ pw.println();
+ pw.println("See " + TimeState.class.getName() + " for more information");
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mUnixEpochTime, 0);
+ dest.writeBoolean(mUserShouldConfirmTime);
+ }
+
+ @NonNull
+ public UnixEpochTime getUnixEpochTime() {
+ return mUnixEpochTime;
+ }
+
+ public boolean getUserShouldConfirmTime() {
+ return mUserShouldConfirmTime;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ TimeState that = (TimeState) o;
+ return Objects.equals(mUnixEpochTime, that.mUnixEpochTime)
+ && mUserShouldConfirmTime == that.mUserShouldConfirmTime;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mUnixEpochTime, mUserShouldConfirmTime);
+ }
+
+ @Override
+ public String toString() {
+ return "TimeState{"
+ + "mUnixEpochTime=" + mUnixEpochTime
+ + ", mUserShouldConfirmTime=" + mUserShouldConfirmTime
+ + '}';
+ }
+}
diff --git a/core/java/android/app/time/TimeZoneCapabilities.java b/core/java/android/app/time/TimeZoneCapabilities.java
index 895a8e491f8d0..5d4629f814937 100644
--- a/core/java/android/app/time/TimeZoneCapabilities.java
+++ b/core/java/android/app/time/TimeZoneCapabilities.java
@@ -22,8 +22,6 @@ 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;
@@ -61,7 +59,7 @@ public final class TimeZoneCapabilities implements Parcelable {
@NonNull private final UserHandle mUserHandle;
private final @CapabilityState int mConfigureAutoDetectionEnabledCapability;
private final @CapabilityState int mConfigureGeoDetectionEnabledCapability;
- private final @CapabilityState int mSuggestManualTimeZoneCapability;
+ private final @CapabilityState int mSetManualTimeZoneCapability;
private TimeZoneCapabilities(@NonNull Builder builder) {
this.mUserHandle = Objects.requireNonNull(builder.mUserHandle);
@@ -69,16 +67,16 @@ public final class TimeZoneCapabilities implements Parcelable {
builder.mConfigureAutoDetectionEnabledCapability;
this.mConfigureGeoDetectionEnabledCapability =
builder.mConfigureGeoDetectionEnabledCapability;
- this.mSuggestManualTimeZoneCapability = builder.mSuggestManualTimeZoneCapability;
+ this.mSetManualTimeZoneCapability = builder.mSetManualTimeZoneCapability;
}
@NonNull
- private static TimeZoneCapabilities createFromParcel(Parcel in) {
+ private static TimeZoneCapabilities createFromParcel(@NonNull Parcel in) {
UserHandle userHandle = UserHandle.readFromParcel(in);
return new TimeZoneCapabilities.Builder(userHandle)
.setConfigureAutoDetectionEnabledCapability(in.readInt())
.setConfigureGeoDetectionEnabledCapability(in.readInt())
- .setSuggestManualTimeZoneCapability(in.readInt())
+ .setSetManualTimeZoneCapability(in.readInt())
.build();
}
@@ -87,7 +85,7 @@ public final class TimeZoneCapabilities implements Parcelable {
UserHandle.writeToParcel(mUserHandle, dest);
dest.writeInt(mConfigureAutoDetectionEnabledCapability);
dest.writeInt(mConfigureGeoDetectionEnabledCapability);
- dest.writeInt(mSuggestManualTimeZoneCapability);
+ dest.writeInt(mSetManualTimeZoneCapability);
}
/**
@@ -112,17 +110,17 @@ public final class TimeZoneCapabilities implements Parcelable {
/**
* Returns the capability state associated with the user's ability to manually set the time zone
- * on a device via {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}.
+ * on a device.
*
- *
The suggestion will be ignored in all cases unless the value is {@link
+ *
The time zone will be ignored in all cases unless the value is {@link
* Capabilities#CAPABILITY_POSSESSED}. See also
* {@link TimeZoneConfiguration#isAutoDetectionEnabled()}.
*
* @hide
*/
@CapabilityState
- public int getSuggestManualTimeZoneCapability() {
- return mSuggestManualTimeZoneCapability;
+ public int getSetManualTimeZoneCapability() {
+ return mSetManualTimeZoneCapability;
}
/**
@@ -174,13 +172,13 @@ public final class TimeZoneCapabilities implements Parcelable {
== that.mConfigureAutoDetectionEnabledCapability
&& mConfigureGeoDetectionEnabledCapability
== that.mConfigureGeoDetectionEnabledCapability
- && mSuggestManualTimeZoneCapability == that.mSuggestManualTimeZoneCapability;
+ && mSetManualTimeZoneCapability == that.mSetManualTimeZoneCapability;
}
@Override
public int hashCode() {
return Objects.hash(mUserHandle, mConfigureAutoDetectionEnabledCapability,
- mConfigureGeoDetectionEnabledCapability, mSuggestManualTimeZoneCapability);
+ mConfigureGeoDetectionEnabledCapability, mSetManualTimeZoneCapability);
}
@Override
@@ -191,17 +189,21 @@ public final class TimeZoneCapabilities implements Parcelable {
+ mConfigureAutoDetectionEnabledCapability
+ ", mConfigureGeoDetectionEnabledCapability="
+ mConfigureGeoDetectionEnabledCapability
- + ", mSuggestManualTimeZoneCapability=" + mSuggestManualTimeZoneCapability
+ + ", mSetManualTimeZoneCapability=" + mSetManualTimeZoneCapability
+ '}';
}
- /** @hide */
+ /**
+ * A builder of {@link TimeZoneCapabilities} objects.
+ *
+ * @hide
+ */
public static class Builder {
@NonNull private UserHandle mUserHandle;
private @CapabilityState int mConfigureAutoDetectionEnabledCapability;
private @CapabilityState int mConfigureGeoDetectionEnabledCapability;
- private @CapabilityState int mSuggestManualTimeZoneCapability;
+ private @CapabilityState int mSetManualTimeZoneCapability;
public Builder(@NonNull UserHandle userHandle) {
mUserHandle = Objects.requireNonNull(userHandle);
@@ -214,25 +216,27 @@ public final class TimeZoneCapabilities implements Parcelable {
capabilitiesToCopy.mConfigureAutoDetectionEnabledCapability;
mConfigureGeoDetectionEnabledCapability =
capabilitiesToCopy.mConfigureGeoDetectionEnabledCapability;
- mSuggestManualTimeZoneCapability =
- capabilitiesToCopy.mSuggestManualTimeZoneCapability;
+ mSetManualTimeZoneCapability =
+ capabilitiesToCopy.mSetManualTimeZoneCapability;
}
- /** Sets the state for the automatic time zone detection enabled config. */
+ /** Sets the value for the "configure automatic time zone detection enabled" capability. */
public Builder setConfigureAutoDetectionEnabledCapability(@CapabilityState int value) {
this.mConfigureAutoDetectionEnabledCapability = value;
return this;
}
- /** Sets the state for the geolocation time zone detection enabled config. */
+ /**
+ * Sets the value for the "configure geolocation time zone detection enabled" capability.
+ */
public Builder setConfigureGeoDetectionEnabledCapability(@CapabilityState int value) {
this.mConfigureGeoDetectionEnabledCapability = value;
return this;
}
- /** Sets the state for the suggestManualTimeZone action. */
- public Builder setSuggestManualTimeZoneCapability(@CapabilityState int value) {
- this.mSuggestManualTimeZoneCapability = value;
+ /** Sets the value for the "set manual time zone" capability. */
+ public Builder setSetManualTimeZoneCapability(@CapabilityState int value) {
+ this.mSetManualTimeZoneCapability = value;
return this;
}
@@ -243,8 +247,8 @@ public final class TimeZoneCapabilities implements Parcelable {
"configureAutoDetectionEnabledCapability");
verifyCapabilitySet(mConfigureGeoDetectionEnabledCapability,
"configureGeoDetectionEnabledCapability");
- verifyCapabilitySet(mSuggestManualTimeZoneCapability,
- "suggestManualTimeZoneCapability");
+ verifyCapabilitySet(mSetManualTimeZoneCapability,
+ "mSetManualTimeZoneCapability");
return new TimeZoneCapabilities(this);
}
diff --git a/core/java/android/app/time/TimeZoneState.aidl b/core/java/android/app/time/TimeZoneState.aidl
new file mode 100644
index 0000000000000..fc1962f3d5b91
--- /dev/null
+++ b/core/java/android/app/time/TimeZoneState.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+parcelable TimeZoneState;
diff --git a/core/java/android/app/time/TimeZoneState.java b/core/java/android/app/time/TimeZoneState.java
new file mode 100644
index 0000000000000..efdff81b079af
--- /dev/null
+++ b/core/java/android/app/time/TimeZoneState.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/**
+ * A snapshot of the system's time zone state.
+ *
+ *
{@code id} contains the system's time zone ID setting, e.g. "America/Los_Angeles". This
+ * will usually agree with {@code TimeZone.getDefault().getID()} but it can be empty in rare cases.
+ *
+ *
{@code userShouldConfirmId} is {@code true} if the system has low confidence in the current
+ * time zone.
+ *
+ * @hide
+ */
+public final class TimeZoneState implements Parcelable {
+
+ public static final @NonNull Creator CREATOR = new Creator<>() {
+ public TimeZoneState createFromParcel(Parcel in) {
+ return TimeZoneState.createFromParcel(in);
+ }
+
+ public TimeZoneState[] newArray(int size) {
+ return new TimeZoneState[size];
+ }
+ };
+
+ @NonNull private final String mId;
+ private final boolean mUserShouldConfirmId;
+
+ /** @hide */
+ public TimeZoneState(@NonNull String id, boolean userShouldConfirmId) {
+ mId = Objects.requireNonNull(id);
+ mUserShouldConfirmId = userShouldConfirmId;
+ }
+
+ private static TimeZoneState createFromParcel(Parcel in) {
+ String zoneId = in.readString();
+ boolean userShouldConfirmId = in.readBoolean();
+ return new TimeZoneState(zoneId, userShouldConfirmId);
+ }
+
+ /** @hide */
+ @Nullable
+ public static TimeZoneState parseCommandLineArgs(@NonNull ShellCommand cmd) {
+ String zoneIdString = null;
+ Boolean userShouldConfirmId = null;
+ String opt;
+ while ((opt = cmd.getNextArg()) != null) {
+ switch (opt) {
+ case "--zone_id": {
+ zoneIdString = cmd.getNextArgRequired();
+ break;
+ }
+ case "--user_should_confirm_id": {
+ userShouldConfirmId = Boolean.parseBoolean(cmd.getNextArgRequired());
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
+ }
+ }
+ if (zoneIdString == null) {
+ throw new IllegalArgumentException("No zoneId specified.");
+ }
+ if (userShouldConfirmId == null) {
+ throw new IllegalArgumentException("No userShouldConfirmId specified.");
+ }
+ return new TimeZoneState(zoneIdString, userShouldConfirmId);
+ }
+
+ /** @hide */
+ public static void printCommandLineOpts(@NonNull PrintWriter pw) {
+ pw.println("TimeZoneState options:");
+ pw.println(" --zone_id {}");
+ pw.println(" --user_should_confirm_id {true|false}");
+ pw.println();
+ pw.println("See " + TimeZoneState.class.getName() + " for more information");
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mId);
+ dest.writeBoolean(mUserShouldConfirmId);
+ }
+
+ @NonNull
+ public String getId() {
+ return mId;
+ }
+
+ public boolean getUserShouldConfirmId() {
+ return mUserShouldConfirmId;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ TimeZoneState that = (TimeZoneState) o;
+ return Objects.equals(mId, that.mId)
+ && mUserShouldConfirmId == that.mUserShouldConfirmId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mId, mUserShouldConfirmId);
+ }
+
+ @Override
+ public String toString() {
+ return "TimeZoneState{"
+ + "mZoneId=" + mId
+ + ", mUserShouldConfirmId=" + mUserShouldConfirmId
+ + '}';
+ }
+}
diff --git a/core/java/android/app/time/UnixEpochTime.aidl b/core/java/android/app/time/UnixEpochTime.aidl
new file mode 100644
index 0000000000000..3392e22580f19
--- /dev/null
+++ b/core/java/android/app/time/UnixEpochTime.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+parcelable UnixEpochTime;
diff --git a/core/java/android/app/time/UnixEpochTime.java b/core/java/android/app/time/UnixEpochTime.java
new file mode 100644
index 0000000000000..2683547e92092
--- /dev/null
+++ b/core/java/android/app/time/UnixEpochTime.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.time;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.os.TimestampedValue;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/**
+ * A Unix epoch time value with an associated reading from the elapsed realtime clock.
+ * When representing a device's system clock time, the Unix epoch time can be obtained using {@link
+ * System#currentTimeMillis()}. The Unix epoch time might also come from an external source
+ * depending on usage.
+ *
+ *
The elapsed realtime clock can be obtained using methods like {@link
+ * SystemClock#elapsedRealtime()} or {@link SystemClock#elapsedRealtimeClock()}.
+ *
+ * @hide
+ */
+public final class UnixEpochTime implements Parcelable {
+ @ElapsedRealtimeLong private final long mElapsedRealtimeMillis;
+ private final long mUnixEpochTimeMillis;
+
+ public UnixEpochTime(@ElapsedRealtimeLong long elapsedRealtimeMillis,
+ long unixEpochTimeMillis) {
+ mElapsedRealtimeMillis = elapsedRealtimeMillis;
+ mUnixEpochTimeMillis = unixEpochTimeMillis;
+ }
+
+ /** @hide */
+ @NonNull
+ public static UnixEpochTime parseCommandLineArgs(ShellCommand cmd) {
+ Long elapsedRealtimeMillis = null;
+ Long unixEpochTimeMillis = null;
+ String opt;
+ while ((opt = cmd.getNextArg()) != null) {
+ switch (opt) {
+ case "--elapsed_realtime": {
+ elapsedRealtimeMillis = Long.parseLong(cmd.getNextArgRequired());
+ break;
+ }
+ case "--unix_epoch_time": {
+ unixEpochTimeMillis = Long.parseLong(cmd.getNextArgRequired());
+ break;
+ }
+ default: {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
+ }
+ }
+
+ if (elapsedRealtimeMillis == null) {
+ throw new IllegalArgumentException("No elapsedRealtimeMillis specified.");
+ }
+ if (unixEpochTimeMillis == null) {
+ throw new IllegalArgumentException("No unixEpochTimeMillis specified.");
+ }
+ return new UnixEpochTime(elapsedRealtimeMillis, unixEpochTimeMillis);
+ }
+
+ /** @hide */
+ public static void printCommandLineOpts(PrintWriter pw) {
+ pw.println("UnixEpochTime options:\n");
+ pw.println(" --elapsed_realtime ");
+ pw.println(" --unix_epoch_time ");
+ pw.println();
+ pw.println("See " + UnixEpochTime.class.getName() + " for more information");
+ }
+
+ /** Returns the elapsed realtime clock value. See {@link UnixEpochTime} for more information. */
+ public @ElapsedRealtimeLong long getElapsedRealtimeMillis() {
+ return mElapsedRealtimeMillis;
+ }
+
+ /** Returns the unix epoch time value. See {@link UnixEpochTime} for more information. */
+ @Nullable
+ public long getUnixEpochTimeMillis() {
+ return mUnixEpochTimeMillis;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ UnixEpochTime that = (UnixEpochTime) o;
+ return mElapsedRealtimeMillis == that.mElapsedRealtimeMillis
+ && Objects.equals(mUnixEpochTimeMillis, that.mUnixEpochTimeMillis);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mElapsedRealtimeMillis, mUnixEpochTimeMillis);
+ }
+
+ @Override
+ public String toString() {
+ return "UnixEpochTime{"
+ + "mElapsedRealtimeTimeMillis=" + mElapsedRealtimeMillis
+ + ", mUnixEpochTimeMillis=" + mUnixEpochTimeMillis
+ + '}';
+ }
+
+ public static final @NonNull Creator CREATOR =
+ new ClassLoaderCreator() {
+
+ @Override
+ public UnixEpochTime createFromParcel(@NonNull Parcel source) {
+ return createFromParcel(source, null);
+ }
+
+ @Override
+ public UnixEpochTime createFromParcel(
+ @NonNull Parcel source, @Nullable ClassLoader classLoader) {
+ long elapsedRealtimeMillis = source.readLong();
+ long unixEpochTimeMillis = source.readLong();
+ return new UnixEpochTime(elapsedRealtimeMillis, unixEpochTimeMillis);
+ }
+
+ @Override
+ public UnixEpochTime[] newArray(int size) {
+ return new UnixEpochTime[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mElapsedRealtimeMillis);
+ dest.writeLong(mUnixEpochTimeMillis);
+ }
+
+ /**
+ * Creates a new Unix epoch time value at {@code elapsedRealtimeTimeMillis} by adjusting this
+ * Unix epoch time by the difference between the elapsed realtime value supplied and the one
+ * associated with this instance.
+ *
+ * @hide
+ */
+ public UnixEpochTime at(@ElapsedRealtimeLong long elapsedRealtimeTimeMillis) {
+ long adjustedUnixEpochTimeMillis =
+ (elapsedRealtimeTimeMillis - mElapsedRealtimeMillis) + mUnixEpochTimeMillis;
+ return new UnixEpochTime(elapsedRealtimeTimeMillis, adjustedUnixEpochTimeMillis);
+ }
+
+ /**
+ * Returns the difference in milliseconds between two instance's elapsed realtimes.
+ *
+ * @hide
+ */
+ public static long elapsedRealtimeDifference(
+ @NonNull UnixEpochTime one, @NonNull UnixEpochTime two) {
+ return one.mElapsedRealtimeMillis - two.mElapsedRealtimeMillis;
+ }
+
+ // TODO(b/246256335) Switch to using UnixEpochTime where possible and remove this method.
+ /** @hide */
+ public TimestampedValue toTimestampedValue() {
+ return new TimestampedValue<>(mElapsedRealtimeMillis, mUnixEpochTimeMillis);
+ }
+}
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl
index 0eb2b5470f58a..a0c898ed2904f 100644
--- a/core/java/android/app/timedetector/ITimeDetectorService.aidl
+++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl
@@ -20,20 +20,23 @@ import android.app.time.ExternalTimeSuggestion;
import android.app.time.ITimeDetectorListener;
import android.app.time.TimeCapabilitiesAndConfig;
import android.app.time.TimeConfiguration;
+import android.app.time.TimeState;
+import android.app.time.UnixEpochTime;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
import android.app.timedetector.TimePoint;
/**
- * System private API to communicate with time detector service.
+ * Binder APIs to communicate with the time detector service.
*
- *
Used by parts of the Android system with signals associated with the device's time to provide
- * information to the Time Detector Service.
- *
- *
Use the {@link android.app.timedetector.TimeDetector} class rather than going through
- * this Binder interface directly. See {@link android.app.timedetector.TimeDetectorService} for
- * more complete documentation.
+ *
Used to provide information to the Time Detector Service from other parts of the Android
+ * system that have access to time-related signals, e.g. telephony. Over time, System APIs have
+ * been added to support unbundled parts of the platform, e.g. SetUp Wizard.
*
+ *
Use the {@link android.app.timedetector.TimeDetector} (internal API) and
+ * {@link android.app.time.TimeManager} (system API) classes rather than going through this Binder
+ * interface directly. See {@link android.app.timedetector.TimeDetectorService} for more complete
+ * documentation.
*
* {@hide}
*/
@@ -44,6 +47,10 @@ interface ITimeDetectorService {
boolean updateConfiguration(in TimeConfiguration timeConfiguration);
+ TimeState getTimeState();
+ boolean confirmTime(in UnixEpochTime time);
+ boolean setManualTime(in ManualTimeSuggestion timeZoneSuggestion);
+
void suggestExternalTime(in ExternalTimeSuggestion timeSuggestion);
boolean suggestManualTime(in ManualTimeSuggestion timeSuggestion);
void suggestTelephonyTime(in TelephonyTimeSuggestion timeSuggestion);
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index db1614b32b597..9d996adc051a9 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -84,6 +84,24 @@ public interface TimeDetector {
*/
String SHELL_COMMAND_SUGGEST_EXTERNAL_TIME = "suggest_external_time";
+ /**
+ * A shell command that retrieves the current system clock time state.
+ * @hide
+ */
+ String SHELL_COMMAND_GET_TIME_STATE = "get_time_state";
+
+ /**
+ * A shell command that sets the current time state for testing.
+ * @hide
+ */
+ String SHELL_COMMAND_SET_TIME_STATE = "set_time_state_for_tests";
+
+ /**
+ * A shell command that sets the confidence in the current time state for testing.
+ * @hide
+ */
+ String SHELL_COMMAND_CONFIRM_TIME = "confirm_time";
+
/**
* A shared utility method to create a {@link ManualTimeSuggestion}.
*
diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
index af0389a14c4bd..47d8e77ae764b 100644
--- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
+++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
@@ -19,16 +19,19 @@ package android.app.timezonedetector;
import android.app.time.ITimeZoneDetectorListener;
import android.app.time.TimeZoneCapabilitiesAndConfig;
import android.app.time.TimeZoneConfiguration;
+import android.app.time.TimeZoneState;
import android.app.timezonedetector.ManualTimeZoneSuggestion;
import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
/**
- * System private API to communicate with time zone detector service.
+ * Binder APIs to communicate with time zone detector service.
*
*
Used to provide information to the Time Zone Detector Service from other parts of the Android
- * system that have access to time zone-related signals, e.g. telephony.
+ * system that have access to time zone-related signals, e.g. telephony. Over time, System APIs have
+ * been added to support unbundled parts of the platform, e.g. SetUp Wizard.
*
- *
Use the {@link android.app.timezonedetector.TimeZoneDetector} class rather than going through
+ *
Use the {@link android.app.timezonedetector.TimeZoneDetector} (internal API) and
+ * {@link android.app.time.TimeManager} (system API) classes rather than going through
* this Binder interface directly. See {@link android.app.timezonedetector.TimeZoneDetectorService}
* for more complete documentation.
*
@@ -41,6 +44,10 @@ interface ITimeZoneDetectorService {
boolean updateConfiguration(in TimeZoneConfiguration configuration);
+ TimeZoneState getTimeZoneState();
+ boolean confirmTimeZone(in String timeZoneId);
+ boolean setManualTimeZone(in ManualTimeZoneSuggestion timeZoneSuggestion);
+
boolean suggestManualTimeZone(in ManualTimeZoneSuggestion timeZoneSuggestion);
void suggestTelephonyTimeZone(in TelephonyTimeZoneSuggestion timeZoneSuggestion);
}
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index bae1c1c7312d1..0e9e28be88188 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -107,6 +107,24 @@ public interface TimeZoneDetector {
*/
String SHELL_COMMAND_ENABLE_TELEPHONY_FALLBACK = "enable_telephony_fallback";
+ /**
+ * A shell command that retrieves the current time zone setting state.
+ * @hide
+ */
+ String SHELL_COMMAND_GET_TIME_ZONE_STATE = "get_time_zone_state";
+
+ /**
+ * A shell command that sets the current time zone state for testing.
+ * @hide
+ */
+ String SHELL_COMMAND_SET_TIME_ZONE_STATE = "set_time_zone_state_for_tests";
+
+ /**
+ * A shell command that sets the confidence in the current time zone state for testing.
+ * @hide
+ */
+ String SHELL_COMMAND_CONFIRM_TIME_ZONE = "confirm_time_zone";
+
/**
* A shell command that dumps a {@link
* com.android.server.timezonedetector.MetricsTimeZoneDetectorState} object to stdout for
diff --git a/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java b/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java
index 9d7dde2141bb6..c9b96c6071ee4 100644
--- a/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java
+++ b/core/tests/coretests/src/android/app/time/TimeCapabilitiesTest.java
@@ -48,10 +48,10 @@ public class TimeCapabilitiesTest {
public void testEquals() {
TimeCapabilities.Builder builder1 = new TimeCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
- .setSuggestManualTimeCapability(CAPABILITY_POSSESSED);
+ .setSetManualTimeCapability(CAPABILITY_POSSESSED);
TimeCapabilities.Builder builder2 = new TimeCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
- .setSuggestManualTimeCapability(CAPABILITY_POSSESSED);
+ .setSetManualTimeCapability(CAPABILITY_POSSESSED);
{
TimeCapabilities one = builder1.build();
TimeCapabilities two = builder2.build();
@@ -72,14 +72,14 @@ public class TimeCapabilitiesTest {
assertEquals(one, two);
}
- builder2.setSuggestManualTimeCapability(CAPABILITY_NOT_ALLOWED);
+ builder2.setSetManualTimeCapability(CAPABILITY_NOT_ALLOWED);
{
TimeCapabilities one = builder1.build();
TimeCapabilities two = builder2.build();
assertNotEquals(one, two);
}
- builder1.setSuggestManualTimeCapability(CAPABILITY_NOT_ALLOWED);
+ builder1.setSetManualTimeCapability(CAPABILITY_NOT_ALLOWED);
{
TimeCapabilities one = builder1.build();
TimeCapabilities two = builder2.build();
@@ -91,12 +91,12 @@ public class TimeCapabilitiesTest {
public void userHandle_notIgnoredInEquals() {
TimeCapabilities firstUserCapabilities = new TimeCapabilities.Builder(UserHandle.of(1))
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
- .setSuggestManualTimeCapability(CAPABILITY_POSSESSED)
+ .setSetManualTimeCapability(CAPABILITY_POSSESSED)
.build();
TimeCapabilities secondUserCapabilities = new TimeCapabilities.Builder(UserHandle.of(2))
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
- .setSuggestManualTimeCapability(CAPABILITY_POSSESSED)
+ .setSetManualTimeCapability(CAPABILITY_POSSESSED)
.build();
assertThat(firstUserCapabilities).isNotEqualTo(secondUserCapabilities);
@@ -106,12 +106,12 @@ public class TimeCapabilitiesTest {
public void testBuilder() {
TimeCapabilities capabilities = new TimeCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_APPLICABLE)
- .setSuggestManualTimeCapability(CAPABILITY_NOT_SUPPORTED)
+ .setSetManualTimeCapability(CAPABILITY_NOT_SUPPORTED)
.build();
assertThat(capabilities.getConfigureAutoDetectionEnabledCapability())
.isEqualTo(CAPABILITY_NOT_APPLICABLE);
- assertThat(capabilities.getSuggestManualTimeCapability())
+ assertThat(capabilities.getSetManualTimeCapability())
.isEqualTo(CAPABILITY_NOT_SUPPORTED);
try {
@@ -133,7 +133,7 @@ public class TimeCapabilitiesTest {
try {
new TimeCapabilities.Builder(TEST_USER_HANDLE)
- .setSuggestManualTimeCapability(CAPABILITY_NOT_APPLICABLE)
+ .setSetManualTimeCapability(CAPABILITY_NOT_APPLICABLE)
.build();
fail("Should throw IllegalStateException");
} catch (IllegalStateException ignored) {
@@ -145,11 +145,11 @@ public class TimeCapabilitiesTest {
public void testParcelable() {
TimeCapabilities.Builder builder = new TimeCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_SUPPORTED)
- .setSuggestManualTimeCapability(CAPABILITY_NOT_SUPPORTED);
+ .setSetManualTimeCapability(CAPABILITY_NOT_SUPPORTED);
assertRoundTripParcelable(builder.build());
- builder.setSuggestManualTimeCapability(CAPABILITY_POSSESSED);
+ builder.setSetManualTimeCapability(CAPABILITY_POSSESSED);
assertRoundTripParcelable(builder.build());
builder.setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED);
@@ -164,7 +164,7 @@ public class TimeCapabilitiesTest {
.build();
TimeCapabilities capabilities = new TimeCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
- .setSuggestManualTimeCapability(CAPABILITY_POSSESSED)
+ .setSetManualTimeCapability(CAPABILITY_POSSESSED)
.build();
TimeConfiguration configChange = new TimeConfiguration.Builder()
@@ -185,7 +185,7 @@ public class TimeCapabilitiesTest {
.build();
TimeCapabilities capabilities = new TimeCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
- .setSuggestManualTimeCapability(CAPABILITY_NOT_ALLOWED)
+ .setSetManualTimeCapability(CAPABILITY_NOT_ALLOWED)
.build();
TimeConfiguration configChange = new TimeConfiguration.Builder()
@@ -199,7 +199,7 @@ public class TimeCapabilitiesTest {
public void copyBuilder_copiesAllFields() {
TimeCapabilities capabilities = new TimeCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
- .setSuggestManualTimeCapability(CAPABILITY_NOT_ALLOWED)
+ .setSetManualTimeCapability(CAPABILITY_NOT_ALLOWED)
.build();
{
@@ -210,7 +210,7 @@ public class TimeCapabilitiesTest {
TimeCapabilities expectedCapabilities =
new TimeCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
- .setSuggestManualTimeCapability(CAPABILITY_NOT_ALLOWED)
+ .setSetManualTimeCapability(CAPABILITY_NOT_ALLOWED)
.build();
assertThat(updatedCapabilities).isEqualTo(expectedCapabilities);
@@ -219,13 +219,13 @@ public class TimeCapabilitiesTest {
{
TimeCapabilities updatedCapabilities =
new TimeCapabilities.Builder(capabilities)
- .setSuggestManualTimeCapability(CAPABILITY_POSSESSED)
+ .setSetManualTimeCapability(CAPABILITY_POSSESSED)
.build();
TimeCapabilities expectedCapabilities =
new TimeCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
- .setSuggestManualTimeCapability(CAPABILITY_POSSESSED)
+ .setSetManualTimeCapability(CAPABILITY_POSSESSED)
.build();
assertThat(updatedCapabilities).isEqualTo(expectedCapabilities);
diff --git a/core/tests/coretests/src/android/app/time/TimeStateTest.java b/core/tests/coretests/src/android/app/time/TimeStateTest.java
new file mode 100644
index 0000000000000..a03229060dfb3
--- /dev/null
+++ b/core/tests/coretests/src/android/app/time/TimeStateTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 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.ShellCommandTestSupport.createShellCommandWithArgsAndOptions;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.Parcel;
+import android.os.ShellCommand;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for non-SDK methods on {@link TimeState}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class TimeStateTest {
+
+ @Test
+ public void testEqualsAndHashcode() {
+ UnixEpochTime time1 = new UnixEpochTime(1, 1);
+ TimeState time1False_1 = new TimeState(time1, false);
+ assertEqualsAndHashCode(time1False_1, time1False_1);
+
+ TimeState time1False_2 = new TimeState(time1, false);
+ assertEqualsAndHashCode(time1False_1, time1False_2);
+
+ TimeState time1True = new TimeState(time1, true);
+ assertNotEquals(time1False_1, time1True);
+
+ UnixEpochTime time2 = new UnixEpochTime(2, 2);
+ TimeState time2False = new TimeState(time2, false);
+ assertNotEquals(time1False_1, time2False);
+ }
+
+ private static void assertEqualsAndHashCode(Object one, Object two) {
+ assertEquals(one, two);
+ assertEquals(one.hashCode(), two.hashCode());
+ }
+
+ @Test
+ public void testParceling() {
+ UnixEpochTime time = new UnixEpochTime(1, 2);
+ TimeState value = new TimeState(time, true);
+ Parcel parcel = Parcel.obtain();
+ try {
+ parcel.writeParcelable(value, 0);
+
+ parcel.setDataPosition(0);
+
+ TimeState stringValueCopy =
+ parcel.readParcelable(null /* classLoader */, TimeState.class);
+ assertEquals(value, stringValueCopy);
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParseCommandLineArg_noElapsedRealtime() {
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ "--unix_epoch_time 12345 --user_should_confirm_time true");
+ TimeState.parseCommandLineArgs(testShellCommand);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParseCommandLineArg_noUnixEpochTime() {
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ "--elapsed_realtime 54321 --user_should_confirm_time true");
+ TimeState.parseCommandLineArgs(testShellCommand);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParseCommandLineArg_noUserShouldConfirmTime() {
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ "--unix_epoch_time 12345 --elapsed_realtime 54321");
+ TimeState.parseCommandLineArgs(testShellCommand);
+ }
+
+ @Test
+ public void testParseCommandLineArg_validSuggestion() {
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ "--elapsed_realtime 54321 --unix_epoch_time 12345 --user_should_confirm_time true");
+ TimeState expectedValue = new TimeState(new UnixEpochTime(54321L, 12345L), true);
+ TimeState actualValue = TimeState.parseCommandLineArgs(testShellCommand);
+ assertEquals(expectedValue, actualValue);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParseCommandLineArg_unknownArgument() {
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ "--elapsed_realtime 54321 --unix_epoch_time 12345 --user_should_confirm_time true"
+ + " --bad_arg 0");
+ TimeState.parseCommandLineArgs(testShellCommand);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java b/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java
index 008272830335a..3f7da8a6fbd06 100644
--- a/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java
+++ b/core/tests/coretests/src/android/app/time/TimeZoneCapabilitiesTest.java
@@ -45,11 +45,11 @@ public class TimeZoneCapabilitiesTest {
TimeZoneCapabilities.Builder builder1 = new TimeZoneCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
.setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED)
- .setSuggestManualTimeZoneCapability(CAPABILITY_POSSESSED);
+ .setSetManualTimeZoneCapability(CAPABILITY_POSSESSED);
TimeZoneCapabilities.Builder builder2 = new TimeZoneCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
.setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED)
- .setSuggestManualTimeZoneCapability(CAPABILITY_POSSESSED);
+ .setSetManualTimeZoneCapability(CAPABILITY_POSSESSED);
{
TimeZoneCapabilities one = builder1.build();
TimeZoneCapabilities two = builder2.build();
@@ -84,14 +84,14 @@ public class TimeZoneCapabilitiesTest {
assertEquals(one, two);
}
- builder2.setSuggestManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED);
+ builder2.setSetManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED);
{
TimeZoneCapabilities one = builder1.build();
TimeZoneCapabilities two = builder2.build();
assertNotEquals(one, two);
}
- builder1.setSuggestManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED);
+ builder1.setSetManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED);
{
TimeZoneCapabilities one = builder1.build();
TimeZoneCapabilities two = builder2.build();
@@ -104,7 +104,7 @@ public class TimeZoneCapabilitiesTest {
TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
.setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED)
- .setSuggestManualTimeZoneCapability(CAPABILITY_POSSESSED);
+ .setSetManualTimeZoneCapability(CAPABILITY_POSSESSED);
assertRoundTripParcelable(builder.build());
builder.setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED);
@@ -113,7 +113,7 @@ public class TimeZoneCapabilitiesTest {
builder.setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED);
assertRoundTripParcelable(builder.build());
- builder.setSuggestManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED);
+ builder.setSetManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED);
assertRoundTripParcelable(builder.build());
}
@@ -127,7 +127,7 @@ public class TimeZoneCapabilitiesTest {
TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
.setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED)
- .setSuggestManualTimeZoneCapability(CAPABILITY_POSSESSED)
+ .setSetManualTimeZoneCapability(CAPABILITY_POSSESSED)
.build();
TimeZoneConfiguration configChange = new TimeZoneConfiguration.Builder()
@@ -150,7 +150,7 @@ public class TimeZoneCapabilitiesTest {
TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
.setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
- .setSuggestManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
+ .setSetManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
.build();
TimeZoneConfiguration configChange = new TimeZoneConfiguration.Builder()
@@ -165,7 +165,7 @@ public class TimeZoneCapabilitiesTest {
TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
.setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
- .setSuggestManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
+ .setSetManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
.build();
{
@@ -177,7 +177,7 @@ public class TimeZoneCapabilitiesTest {
new TimeZoneCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
.setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
- .setSuggestManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
+ .setSetManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
.build();
assertThat(updatedCapabilities).isEqualTo(expectedCapabilities);
@@ -193,7 +193,7 @@ public class TimeZoneCapabilitiesTest {
new TimeZoneCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
.setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED)
- .setSuggestManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
+ .setSetManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
.build();
assertThat(updatedCapabilities).isEqualTo(expectedCapabilities);
@@ -202,14 +202,14 @@ public class TimeZoneCapabilitiesTest {
{
TimeZoneCapabilities updatedCapabilities =
new TimeZoneCapabilities.Builder(capabilities)
- .setSuggestManualTimeZoneCapability(CAPABILITY_POSSESSED)
+ .setSetManualTimeZoneCapability(CAPABILITY_POSSESSED)
.build();
TimeZoneCapabilities expectedCapabilities =
new TimeZoneCapabilities.Builder(TEST_USER_HANDLE)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
.setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
- .setSuggestManualTimeZoneCapability(CAPABILITY_POSSESSED)
+ .setSetManualTimeZoneCapability(CAPABILITY_POSSESSED)
.build();
assertThat(updatedCapabilities).isEqualTo(expectedCapabilities);
diff --git a/core/tests/coretests/src/android/app/time/TimeZoneStateTest.java b/core/tests/coretests/src/android/app/time/TimeZoneStateTest.java
new file mode 100644
index 0000000000000..9786bb044cb27
--- /dev/null
+++ b/core/tests/coretests/src/android/app/time/TimeZoneStateTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 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.ShellCommandTestSupport.createShellCommandWithArgsAndOptions;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.Parcel;
+import android.os.ShellCommand;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for non-SDK methods on {@link TimeZoneState}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class TimeZoneStateTest {
+
+ @Test
+ public void testEqualsAndHashcode() {
+ String zone1 = "Europe/London";
+ TimeZoneState zone1False_1 = new TimeZoneState(zone1, false);
+ assertEqualsAndHashCode(zone1False_1, zone1False_1);
+
+ TimeZoneState zone1False_2 = new TimeZoneState(zone1, false);
+ assertEqualsAndHashCode(zone1False_1, zone1False_2);
+
+ TimeZoneState zone1True = new TimeZoneState(zone1, true);
+ assertNotEquals(zone1False_1, zone1True);
+
+ String zone2 = "Europe/Parise";
+ TimeZoneState zone2False = new TimeZoneState(zone2, false);
+ assertNotEquals(zone1False_1, zone2False);
+ }
+
+ private static void assertEqualsAndHashCode(Object one, Object two) {
+ assertEquals(one, two);
+ assertEquals(one.hashCode(), two.hashCode());
+ }
+
+ @Test
+ public void testParceling() {
+ TimeZoneState value = new TimeZoneState("Europe/London", true);
+ Parcel parcel = Parcel.obtain();
+ try {
+ parcel.writeParcelable(value, 0);
+
+ parcel.setDataPosition(0);
+
+ TimeZoneState stringValueCopy =
+ parcel.readParcelable(null /* classLoader */, TimeZoneState.class);
+ assertEquals(value, stringValueCopy);
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParseCommandLineArg_noZoneId() {
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ "--user_should_confirm_id true");
+ TimeZoneState.parseCommandLineArgs(testShellCommand);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParseCommandLineArg_noUserShouldConfirmId() {
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ "--zone_id Europe/London");
+ TimeZoneState.parseCommandLineArgs(testShellCommand);
+ }
+
+ @Test
+ public void testParseCommandLineArg_validSuggestion() {
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ "--zone_id Europe/London --user_should_confirm_id true");
+ TimeZoneState expectedValue = new TimeZoneState("Europe/London", true);
+ TimeZoneState actualValue = TimeZoneState.parseCommandLineArgs(testShellCommand);
+ assertEquals(expectedValue, actualValue);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParseCommandLineArg_unknownArgument() {
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ "--zone_id Europe/London --user_should_confirm_id true --bad_arg 0");
+ TimeZoneState.parseCommandLineArgs(testShellCommand);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/time/UnixEpochTimeTest.java b/core/tests/coretests/src/android/app/time/UnixEpochTimeTest.java
new file mode 100644
index 0000000000000..cd753489b50e8
--- /dev/null
+++ b/core/tests/coretests/src/android/app/time/UnixEpochTimeTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 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.ShellCommandTestSupport.createShellCommandWithArgsAndOptions;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.os.Parcel;
+import android.os.ShellCommand;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for non-SDK methods on {@link UnixEpochTime}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class UnixEpochTimeTest {
+
+ @Test
+ public void testEqualsAndHashcode() {
+ UnixEpochTime one1000one = new UnixEpochTime(1000, 1);
+ assertEqualsAndHashCode(one1000one, one1000one);
+
+ UnixEpochTime one1000two = new UnixEpochTime(1000, 1);
+ assertEqualsAndHashCode(one1000one, one1000two);
+
+ UnixEpochTime two1000 = new UnixEpochTime(1000, 2);
+ assertNotEquals(one1000one, two1000);
+
+ UnixEpochTime one2000 = new UnixEpochTime(2000, 1);
+ assertNotEquals(one1000one, one2000);
+ }
+
+ private static void assertEqualsAndHashCode(Object one, Object two) {
+ assertEquals(one, two);
+ assertEquals(one.hashCode(), two.hashCode());
+ }
+
+ @Test
+ public void testParceling() {
+ UnixEpochTime value = new UnixEpochTime(1000, 1);
+ Parcel parcel = Parcel.obtain();
+ try {
+ parcel.writeParcelable(value, 0);
+
+ parcel.setDataPosition(0);
+
+ UnixEpochTime stringValueCopy =
+ parcel.readParcelable(null /* classLoader */, UnixEpochTime.class);
+ assertEquals(value, stringValueCopy);
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParseCommandLineArg_noElapsedRealtime() {
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ "--unix_epoch_time 12345");
+ UnixEpochTime.parseCommandLineArgs(testShellCommand);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParseCommandLineArg_noUnixEpochTime() {
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ "--elapsed_realtime 54321");
+ UnixEpochTime.parseCommandLineArgs(testShellCommand);
+ }
+
+ @Test
+ public void testParseCommandLineArg_validSuggestion() {
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ "--elapsed_realtime 54321 --unix_epoch_time 12345");
+ UnixEpochTime expectedValue = new UnixEpochTime(54321L, 12345L);
+ UnixEpochTime actualValue = UnixEpochTime.parseCommandLineArgs(testShellCommand);
+ assertEquals(expectedValue, actualValue);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testParseCommandLineArg_unknownArgument() {
+ ShellCommand testShellCommand = createShellCommandWithArgsAndOptions(
+ "--elapsed_realtime 54321 --unix_epoch_time 12345 --bad_arg 0");
+ UnixEpochTime.parseCommandLineArgs(testShellCommand);
+ }
+
+ @Test
+ public void testAt() {
+ long timeMillis = 1000L;
+ int elapsedRealtimeMillis = 100;
+ UnixEpochTime unixEpochTime = new UnixEpochTime(elapsedRealtimeMillis, timeMillis);
+ // Reference time is after the timestamp.
+ UnixEpochTime at125 = unixEpochTime.at(125);
+ assertEquals(timeMillis + (125 - elapsedRealtimeMillis), at125.getUnixEpochTimeMillis());
+ assertEquals(125, at125.getElapsedRealtimeMillis());
+
+ // Reference time is before the timestamp.
+ UnixEpochTime at75 = unixEpochTime.at(75);
+ assertEquals(timeMillis + (75 - elapsedRealtimeMillis), at75.getUnixEpochTimeMillis());
+ assertEquals(75, at75.getElapsedRealtimeMillis());
+ }
+
+ @Test
+ public void testElapsedRealtimeDifference() {
+ UnixEpochTime value1 = new UnixEpochTime(1000, 123L);
+ assertEquals(0, UnixEpochTime.elapsedRealtimeDifference(value1, value1));
+
+ UnixEpochTime value2 = new UnixEpochTime(1, 321L);
+ assertEquals(999, UnixEpochTime.elapsedRealtimeDifference(value1, value2));
+ assertEquals(-999, UnixEpochTime.elapsedRealtimeDifference(value2, value1));
+ }
+}
diff --git a/services/core/java/com/android/server/timedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java
index 46f335ef9fa2d..d9a4266e28127 100644
--- a/services/core/java/com/android/server/timedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timedetector/ConfigurationInternal.java
@@ -46,7 +46,7 @@ public final class ConfigurationInternal {
private final boolean mAutoDetectionSupported;
private final int mSystemClockUpdateThresholdMillis;
- private final int mSystemClockConfidenceUpgradeThresholdMillis;
+ private final int mSystemClockConfidenceThresholdMillis;
private final Instant mAutoSuggestionLowerBound;
private final Instant mManualSuggestionLowerBound;
private final Instant mSuggestionUpperBound;
@@ -58,8 +58,8 @@ public final class ConfigurationInternal {
private ConfigurationInternal(Builder builder) {
mAutoDetectionSupported = builder.mAutoDetectionSupported;
mSystemClockUpdateThresholdMillis = builder.mSystemClockUpdateThresholdMillis;
- mSystemClockConfidenceUpgradeThresholdMillis =
- builder.mSystemClockConfidenceUpgradeThresholdMillis;
+ mSystemClockConfidenceThresholdMillis =
+ builder.mSystemClockConfidenceThresholdMillis;
mAutoSuggestionLowerBound = Objects.requireNonNull(builder.mAutoSuggestionLowerBound);
mManualSuggestionLowerBound = Objects.requireNonNull(builder.mManualSuggestionLowerBound);
mSuggestionUpperBound = Objects.requireNonNull(builder.mSuggestionUpperBound);
@@ -85,14 +85,14 @@ public final class ConfigurationInternal {
}
/**
- * Return the absolute threshold at/below which the system clock confidence can be upgraded.
- * i.e. if the detector receives a high-confidence time and the current system clock is +/- this
- * value from that time and the confidence in the time is low, then the device's confidence in
- * the current system clock time can be upgraded. This needs to be an amount users would
- * consider "close enough".
+ * Return the absolute threshold for Unix epoch time comparison at/below which the system clock
+ * confidence can be said to be "close enough", e.g. if the detector receives a high-confidence
+ * time and the current system clock is +/- this value from that time and the current confidence
+ * in the time is low, then the device's confidence in the current system clock time can be
+ * upgraded.
*/
- public int getSystemClockConfidenceUpgradeThresholdMillis() {
- return mSystemClockConfidenceUpgradeThresholdMillis;
+ public int getSystemClockConfidenceThresholdMillis() {
+ return mSystemClockConfidenceThresholdMillis;
}
/**
@@ -194,7 +194,7 @@ public final class ConfigurationInternal {
} else {
suggestManualTimeZoneCapability = CAPABILITY_POSSESSED;
}
- builder.setSuggestManualTimeCapability(suggestManualTimeZoneCapability);
+ builder.setSetManualTimeCapability(suggestManualTimeZoneCapability);
return builder.build();
}
@@ -256,8 +256,8 @@ public final class ConfigurationInternal {
return "ConfigurationInternal{"
+ "mAutoDetectionSupported=" + mAutoDetectionSupported
+ ", mSystemClockUpdateThresholdMillis=" + mSystemClockUpdateThresholdMillis
- + ", mSystemClockConfidenceUpgradeThresholdMillis="
- + mSystemClockConfidenceUpgradeThresholdMillis
+ + ", mSystemClockConfidenceThresholdMillis="
+ + mSystemClockConfidenceThresholdMillis
+ ", mAutoSuggestionLowerBound=" + mAutoSuggestionLowerBound
+ "(" + mAutoSuggestionLowerBound.toEpochMilli() + ")"
+ ", mManualSuggestionLowerBound=" + mManualSuggestionLowerBound
@@ -274,7 +274,7 @@ public final class ConfigurationInternal {
static final class Builder {
private boolean mAutoDetectionSupported;
private int mSystemClockUpdateThresholdMillis;
- private int mSystemClockConfidenceUpgradeThresholdMillis;
+ private int mSystemClockConfidenceThresholdMillis;
@NonNull private Instant mAutoSuggestionLowerBound;
@NonNull private Instant mManualSuggestionLowerBound;
@NonNull private Instant mSuggestionUpperBound;
@@ -321,9 +321,9 @@ public final class ConfigurationInternal {
return this;
}
- /** See {@link ConfigurationInternal#getSystemClockConfidenceUpgradeThresholdMillis()}. */
- public Builder setSystemClockConfidenceUpgradeThresholdMillis(int thresholdMillis) {
- mSystemClockConfidenceUpgradeThresholdMillis = thresholdMillis;
+ /** See {@link ConfigurationInternal#getSystemClockConfidenceThresholdMillis()}. */
+ public Builder setSystemClockConfidenceThresholdMillis(int thresholdMillis) {
+ mSystemClockConfidenceThresholdMillis = thresholdMillis;
return this;
}
diff --git a/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java
index 0ea5f7a105d13..84013a7550354 100644
--- a/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java
+++ b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java
@@ -245,7 +245,7 @@ final class ServiceConfigAccessorImpl implements ServiceConfigAccessor {
.setAutoDetectionSupported(isAutoDetectionSupported())
.setAutoDetectionEnabledSetting(getAutoDetectionEnabledSetting())
.setSystemClockUpdateThresholdMillis(getSystemClockUpdateThresholdMillis())
- .setSystemClockConfidenceUpgradeThresholdMillis(
+ .setSystemClockConfidenceThresholdMillis(
getSystemClockConfidenceUpgradeThresholdMillis())
.setAutoSuggestionLowerBound(getAutoSuggestionLowerBound())
.setManualSuggestionLowerBound(timeDetectorHelper.getManualSuggestionLowerBound())
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 5c47abfff09eb..4186330fd23c2 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -24,6 +24,8 @@ import android.app.time.ExternalTimeSuggestion;
import android.app.time.ITimeDetectorListener;
import android.app.time.TimeCapabilitiesAndConfig;
import android.app.time.TimeConfiguration;
+import android.app.time.TimeState;
+import android.app.time.UnixEpochTime;
import android.app.timedetector.ITimeDetectorService;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
@@ -271,6 +273,58 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub
}
}
+ @Override
+ public TimeState getTimeState() {
+ enforceManageTimeDetectorPermission();
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mTimeDetectorStrategy.getTimeState();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ void setTimeState(@NonNull TimeState timeState) {
+ enforceManageTimeDetectorPermission();
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mTimeDetectorStrategy.setTimeState(timeState);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public boolean confirmTime(@NonNull UnixEpochTime time) {
+ enforceManageTimeDetectorPermission();
+ Objects.requireNonNull(time);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mTimeDetectorStrategy.confirmTime(time);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public boolean setManualTime(@NonNull ManualTimeSuggestion timeSignal) {
+ enforceManageTimeDetectorPermission();
+ Objects.requireNonNull(timeSignal);
+
+ // This calls suggestManualTime() as the logic is identical, it only differs in the
+ // permission required, which is handled on the line above.
+ int userId = mCallerIdentityInjector.getCallingUserId();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return mTimeDetectorStrategy.suggestManualTime(userId, timeSignal);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
@Override
public void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSignal) {
enforceSuggestTelephonyTimePermission();
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java
index d306d10f3c697..990c00feae16b 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorShellCommand.java
@@ -15,9 +15,12 @@
*/
package com.android.server.timedetector;
+import static android.app.timedetector.TimeDetector.SHELL_COMMAND_CONFIRM_TIME;
+import static android.app.timedetector.TimeDetector.SHELL_COMMAND_GET_TIME_STATE;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_IS_AUTO_DETECTION_ENABLED;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SERVICE_NAME;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SET_AUTO_DETECTION_ENABLED;
+import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SET_TIME_STATE;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_EXTERNAL_TIME;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_GNSS_TIME;
import static android.app.timedetector.TimeDetector.SHELL_COMMAND_SUGGEST_MANUAL_TIME;
@@ -30,6 +33,8 @@ import static com.android.server.timedetector.ServerFlags.KEY_TIME_DETECTOR_ORIG
import android.app.time.ExternalTimeSuggestion;
import android.app.time.TimeConfiguration;
+import android.app.time.TimeState;
+import android.app.time.UnixEpochTime;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.TelephonyTimeSuggestion;
import android.os.ShellCommand;
@@ -69,6 +74,12 @@ class TimeDetectorShellCommand extends ShellCommand {
return runSuggestGnssTime();
case SHELL_COMMAND_SUGGEST_EXTERNAL_TIME:
return runSuggestExternalTime();
+ case SHELL_COMMAND_GET_TIME_STATE:
+ return runGetTimeState();
+ case SHELL_COMMAND_SET_TIME_STATE:
+ return runSetTimeState();
+ case SHELL_COMMAND_CONFIRM_TIME:
+ return runConfirmTime();
default: {
return handleDefaultCommands(cmd);
}
@@ -140,6 +151,24 @@ class TimeDetectorShellCommand extends ShellCommand {
}
}
+ private int runGetTimeState() {
+ TimeState timeState = mInterface.getTimeState();
+ getOutPrintWriter().println(timeState);
+ return 0;
+ }
+
+ private int runSetTimeState() {
+ TimeState timeState = TimeState.parseCommandLineArgs(this);
+ mInterface.setTimeState(timeState);
+ return 0;
+ }
+
+ private int runConfirmTime() {
+ UnixEpochTime unixEpochTime = UnixEpochTime.parseCommandLineArgs(this);
+ getOutPrintWriter().println(mInterface.confirmTime(unixEpochTime));
+ return 0;
+ }
+
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -161,6 +190,12 @@ class TimeDetectorShellCommand extends ShellCommand {
pw.printf(" Suggests a time as if via the \"gnss\" origin.\n");
pw.printf(" %s \n", SHELL_COMMAND_SUGGEST_EXTERNAL_TIME);
pw.printf(" Suggests a time as if via the \"external\" origin.\n");
+ pw.printf(" %s\n", SHELL_COMMAND_GET_TIME_STATE);
+ pw.printf(" Returns the current time setting state.\n");
+ pw.printf(" %s