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