Add LTZP status APIs to the SDK

Give TimeZoneProviderService implementations the ability to supply
(optional) status information. This status information can be used for
two purposes:
1) To help report status to users in the SettingsUI (once plumbed
   through).
2) To influence higher-level time zone detection behavior, i.e. so that
   the time zone detection can understand that a TZP is unable to
   perform its duties, perhaps something the platform cannot detect for
   itself.

Test: atest core/tests/coretests/src/android/service/timezone/
Test: atest services/tests/servicestests/src/com/android/server/timezonedetector/location/
Bug: 236624675
Change-Id: Ifa66b8c968c0bdf31b6bdf55ead34b3fa1d9e6fa
This commit is contained in:
Neil Fuller
2022-10-19 18:51:45 +01:00
parent 161e9f1bb7
commit e13dc3f47b
4 changed files with 40 additions and 94 deletions

View File

@@ -11889,11 +11889,39 @@ package android.service.timezone {
method public abstract void onStopUpdates();
method public final void reportPermanentFailure(@NonNull Throwable);
method public final void reportSuggestion(@NonNull android.service.timezone.TimeZoneProviderSuggestion);
method public final void reportSuggestion(@NonNull android.service.timezone.TimeZoneProviderSuggestion, @NonNull android.service.timezone.TimeZoneProviderStatus);
method public final void reportUncertain();
method public final void reportUncertain(@NonNull android.service.timezone.TimeZoneProviderStatus);
field public static final String PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE = "android.service.timezone.PrimaryLocationTimeZoneProviderService";
field public static final String SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE = "android.service.timezone.SecondaryLocationTimeZoneProviderService";
}
public final class TimeZoneProviderStatus implements android.os.Parcelable {
method public int describeContents();
method public int getConnectivityDependencyStatus();
method public int getLocationDetectionDependencyStatus();
method public int getTimeZoneResolutionOperationStatus();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.timezone.TimeZoneProviderStatus> CREATOR;
field public static final int DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT = 4; // 0x4
field public static final int DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS = 6; // 0x6
field public static final int DEPENDENCY_STATUS_DEGRADED_BY_SETTINGS = 5; // 0x5
field public static final int DEPENDENCY_STATUS_NOT_APPLICABLE = 1; // 0x1
field public static final int DEPENDENCY_STATUS_OK = 2; // 0x2
field public static final int DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE = 3; // 0x3
field public static final int OPERATION_STATUS_FAILED = 3; // 0x3
field public static final int OPERATION_STATUS_NOT_APPLICABLE = 1; // 0x1
field public static final int OPERATION_STATUS_OK = 2; // 0x2
}
public static final class TimeZoneProviderStatus.Builder {
ctor public TimeZoneProviderStatus.Builder();
method @NonNull public android.service.timezone.TimeZoneProviderStatus build();
method @NonNull public android.service.timezone.TimeZoneProviderStatus.Builder setConnectivityDependencyStatus(int);
method @NonNull public android.service.timezone.TimeZoneProviderStatus.Builder setLocationDetectionDependencyStatus(int);
method @NonNull public android.service.timezone.TimeZoneProviderStatus.Builder setTimeZoneResolutionOperationStatus(int);
}
public final class TimeZoneProviderSuggestion implements android.os.Parcelable {
method public int describeContents();
method public long getElapsedRealtimeMillis();

View File

@@ -44,8 +44,8 @@ import java.util.Objects;
*
* <p>Once started, providers are expected to detect the time zone if possible, and report the
* result via {@link #reportSuggestion(TimeZoneProviderSuggestion)} or {@link
* #reportUncertain()}. Providers may also report that they have permanently failed
* by calling {@link #reportPermanentFailure(Throwable)}. See the javadocs for each
* #reportUncertain(TimeZoneProviderStatus)}. Providers may also report that they have permanently
* failed by calling {@link #reportPermanentFailure(Throwable)}. See the javadocs for each
* method for details.
*
* <p>After starting, providers are expected to issue their first callback within the timeout
@@ -213,8 +213,6 @@ public abstract class TimeZoneProviderService extends Service {
*
* @param providerStatus provider status information that can influence detector service
* behavior and/or be reported via the device UI
*
* @hide
*/
public final void reportSuggestion(@NonNull TimeZoneProviderSuggestion suggestion,
@NonNull TimeZoneProviderStatus providerStatus) {
@@ -248,8 +246,9 @@ public abstract class TimeZoneProviderService extends Service {
/**
* Indicates the time zone is not known because of an expected runtime state or error, e.g. when
* the provider is unable to detect location, or there was a problem when resolving the location
* to a time zone.
* the provider is unable to detect location, or there was connectivity issue.
*
* <p>See {@link #reportUncertain(TimeZoneProviderStatus)} for a more expressive version
*/
public final void reportUncertain() {
TimeZoneProviderStatus providerStatus = null;
@@ -264,8 +263,6 @@ public abstract class TimeZoneProviderService extends Service {
*
* @param providerStatus provider status information that can influence detector service
* behavior and/or be reported via the device UI
*
* @hide
*/
public final void reportUncertain(@NonNull TimeZoneProviderStatus providerStatus) {
Objects.requireNonNull(providerStatus);
@@ -362,8 +359,8 @@ public abstract class TimeZoneProviderService extends Service {
* <p>Between {@link #onStartUpdates(long)} and {@link #onStopUpdates()} calls, the Android
* system server holds the latest report from the provider in memory. After an initial report,
* provider implementations are only required to send a report via {@link
* #reportSuggestion(TimeZoneProviderSuggestion)} or via {@link #reportUncertain()} when it
* differs from the previous report.
* #reportSuggestion(TimeZoneProviderSuggestion, TimeZoneProviderStatus)} or via {@link
* #reportUncertain(TimeZoneProviderStatus)} when it differs from the previous report.
*
* <p>{@link #reportPermanentFailure(Throwable)} can also be called by provider implementations
* in rare cases, after which the provider should consider itself stopped and not make any
@@ -375,7 +372,8 @@ public abstract class TimeZoneProviderService extends Service {
* Android system server may move on to use other providers or detection methods. Providers
* should therefore make best efforts during this time to generate a report, which could involve
* increased power usage. Providers should preferably report an explicit {@link
* #reportUncertain()} if the time zone(s) cannot be detected within the initialization timeout.
* #reportUncertain(TimeZoneProviderStatus)} if the time zone(s) cannot be detected within the
* initialization timeout.
*
* @see #onStopUpdates() for the signal from the system server to stop sending reports
*/

View File

@@ -19,6 +19,7 @@ package android.service.timezone;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -65,6 +66,7 @@ import java.util.regex.Pattern;
*
* @hide
*/
@SystemApi
public final class TimeZoneProviderStatus implements Parcelable {
/**

View File

@@ -16,20 +16,15 @@
package android.service.timezone;
import static android.app.time.ParcelableTestSupport.assertRoundTripParcelable;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS;
import static android.service.timezone.TimeZoneProviderStatus.DEPENDENCY_STATUS_OK;
import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_FAILED;
import static android.service.timezone.TimeZoneProviderStatus.OPERATION_STATUS_OK;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertThrows;
import org.junit.Test;
/** Non-SDK tests. See CTS for SDK API tests. */
public class TimeZoneProviderStatusTest {
@Test
@@ -42,81 +37,4 @@ public class TimeZoneProviderStatusTest {
assertEquals(status, TimeZoneProviderStatus.parseProviderStatus(status.toString()));
}
@Test
public void testStatusValidation() {
TimeZoneProviderStatus status = new TimeZoneProviderStatus.Builder()
.setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
.setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
.setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
.build();
assertThrows(IllegalArgumentException.class,
() -> new TimeZoneProviderStatus.Builder(status)
.setLocationDetectionDependencyStatus(-1)
.build());
assertThrows(IllegalArgumentException.class,
() -> new TimeZoneProviderStatus.Builder(status)
.setConnectivityDependencyStatus(-1)
.build());
assertThrows(IllegalArgumentException.class,
() -> new TimeZoneProviderStatus.Builder(status)
.setTimeZoneResolutionOperationStatus(-1)
.build());
}
@Test
public void testEqualsAndHashcode() {
TimeZoneProviderStatus status1_1 = new TimeZoneProviderStatus.Builder()
.setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
.setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
.setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
.build();
assertEqualsAndHashcode(status1_1, status1_1);
assertNotEquals(status1_1, null);
{
TimeZoneProviderStatus status1_2 =
new TimeZoneProviderStatus.Builder(status1_1).build();
assertEqualsAndHashcode(status1_1, status1_2);
assertNotSame(status1_1, status1_2);
}
{
TimeZoneProviderStatus status2 = new TimeZoneProviderStatus.Builder(status1_1)
.setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT)
.build();
assertNotEquals(status1_1, status2);
}
{
TimeZoneProviderStatus status2 = new TimeZoneProviderStatus.Builder(status1_1)
.setConnectivityDependencyStatus(DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT)
.build();
assertNotEquals(status1_1, status2);
}
{
TimeZoneProviderStatus status2 = new TimeZoneProviderStatus.Builder(status1_1)
.setTimeZoneResolutionOperationStatus(OPERATION_STATUS_FAILED)
.build();
assertNotEquals(status1_1, status2);
}
}
private static void assertEqualsAndHashcode(Object one, Object two) {
assertEquals(one, two);
assertEquals(two, one);
assertEquals(one.hashCode(), two.hashCode());
}
@Test
public void testParcelable() {
TimeZoneProviderStatus status = new TimeZoneProviderStatus.Builder()
.setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
.setConnectivityDependencyStatus(DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT)
.setTimeZoneResolutionOperationStatus(OPERATION_STATUS_FAILED)
.build();
assertRoundTripParcelable(status);
}
}