Merge "Add System APIs for TimeZoneProvider"

This commit is contained in:
Neil Fuller
2020-12-16 13:21:54 +00:00
committed by Android (Google) Code Review
25 changed files with 1489 additions and 552 deletions

View File

@@ -9814,6 +9814,38 @@ package android.service.textclassifier {
}
package android.service.timezone {
public abstract class TimeZoneProviderService extends android.app.Service {
ctor public TimeZoneProviderService();
method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onStartUpdates(long);
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 reportUncertain();
field public static final String BIND_PERMISSION = "android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER";
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 TimeZoneProviderSuggestion implements android.os.Parcelable {
method public int describeContents();
method public long getElapsedRealtimeMillis();
method @NonNull public java.util.List<java.lang.String> getTimeZoneIds();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.service.timezone.TimeZoneProviderSuggestion> CREATOR;
}
public static final class TimeZoneProviderSuggestion.Builder {
ctor public TimeZoneProviderSuggestion.Builder();
method @NonNull public android.service.timezone.TimeZoneProviderSuggestion build();
method @NonNull public android.service.timezone.TimeZoneProviderSuggestion.Builder setElapsedRealtimeMillis(long);
method @NonNull public android.service.timezone.TimeZoneProviderSuggestion.Builder setTimeZoneIds(@NonNull java.util.List<java.lang.String>);
}
}
package android.service.trust {
public class TrustAgentService extends android.app.Service {

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2020 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.service.timezone;
import android.service.timezone.ITimeZoneProviderManager;
/**
* @hide
*/
oneway interface ITimeZoneProvider {
void setTimeZoneProviderManager(in @nullable ITimeZoneProviderManager manager);
void startUpdates(in long initializationTimeoutMillis);
void stopUpdates();
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2020 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.service.timezone;
import android.service.timezone.TimeZoneProviderSuggestion;
/**
* @hide
*/
oneway interface ITimeZoneProviderManager {
void onTimeZoneProviderSuggestion(in TimeZoneProviderSuggestion timeZoneProviderSuggestion);
void onTimeZoneProviderUncertain();
void onTimeZoneProviderPermanentFailure(in String failureReason);
}

View File

@@ -0,0 +1,225 @@
/*
* Copyright (C) 2020 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.service.timezone;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.os.BackgroundThread;
import java.util.Objects;
/**
* A service to generate time zone callbacks to the platform. Developers must extend this class.
*
* <p>Provider implementations are started via a call to {@link #onStartUpdates(long)} and stopped
* via a call to {@link #onStopUpdates()}.
*
* <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
* method for details.
*
* <p>After starting, providers are expected to issue their first callback within the timeout
* duration specified in {@link #onStartUpdates(long)}, or they will be implicitly considered to be
* uncertain.
*
* <p>Once stopped or failed, providers are required to stop generating callbacks.
*
* <p>Provider discovery:
*
* <p>You must declare the service in your manifest file with the
* {@link android.Manifest.permission#INSTALL_LOCATION_TIME_ZONE_PROVIDER} permission,
* and include an intent filter with the necessary action indicating what type of provider it is.
*
* <p>Device configuration can influence how {@link TimeZoneProviderService}s are discovered.
* In one mode, there can be multiple {@link TimeZoneProviderService}s configured with the same
* action, and the one with the highest "serviceVersion" metadata will be used.
*
* <p>{@link TimeZoneProviderService}s may be deployed into processes that run once-per-user
* or once-per-device (i.e. they service multiple users). The "serviceIsMultiuser" metadata must
* be set accordingly.
*
* <p>Provider types:
*
* <p>Android currently supports up to two location-derived time zone providers. These are called
* the "primary" and "secondary" location time zone provider, configured using {@link
* #PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE} and {@link
* #SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE} respectively. The primary location time
* zone provider is started first and will be used until becomes uncertain or fails, at which point
* the secondary provider will be started.
*
* For example:
* <pre>
* &lt;service android:name=".FooTimeZoneProviderService"
* android:exported="true"
* android:permission="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER"&gt;
* &lt;intent-filter&gt;
* &lt;action
* android:name="android.service.timezone.SecondaryLocationTimeZoneProviderService"
* /&gt;
* &lt;/intent-filter&gt;
* &lt;meta-data android:name="serviceVersion" android:value="1" /&gt;
* &lt;meta-data android:name="serviceIsMultiuser" android:value="true" /&gt;
* &lt;/service&gt;
* </pre>
*
*
* <p>Threading:
*
* <p>Calls to {@code report} methods can be made on on any thread and will be passed asynchronously
* to the system server. Calls to {@link #onStartUpdates(long)} and {@link #onStopUpdates()} will
* occur on a single thread.
*
* @hide
*/
@SystemApi
public abstract class TimeZoneProviderService extends Service {
private static final String TAG = "TimeZoneProviderService";
private final Handler mHandler = BackgroundThread.getHandler();
/**
* The Intent action that the primary location-derived time zone provider service must respond
* to. Add it to the intent filter of the service in its manifest.
*/
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE =
"android.service.timezone.PrimaryLocationTimeZoneProviderService";
/**
* The Intent action that the secondary location-based time zone provider service must respond
* to. Add it to the intent filter of the service in its manifest.
*/
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE =
"android.service.timezone.SecondaryLocationTimeZoneProviderService";
/**
* The permission that a service must require to ensure that only Android system can bind to it.
* If this permission is not enforced in the AndroidManifest of the service, the system will
* skip that service.
*/
public static final String BIND_PERMISSION =
"android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER";
private final TimeZoneProviderServiceWrapper mWrapper = new TimeZoneProviderServiceWrapper();
/** Set by {@link #mHandler} thread. */
@Nullable
private ITimeZoneProviderManager mManager;
@Override
@NonNull
public final IBinder onBind(@NonNull Intent intent) {
return mWrapper;
}
/**
* Indicates a successful time zone detection. See {@link TimeZoneProviderSuggestion} for
* details.
*/
public final void reportSuggestion(@NonNull TimeZoneProviderSuggestion suggestion) {
Objects.requireNonNull(suggestion);
mHandler.post(() -> {
ITimeZoneProviderManager manager = mManager;
if (manager != null) {
try {
manager.onTimeZoneProviderSuggestion(suggestion);
} catch (RemoteException | RuntimeException e) {
Log.w(TAG, e);
}
}
});
}
/**
* 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.
*/
public final void reportUncertain() {
mHandler.post(() -> {
ITimeZoneProviderManager manager = mManager;
if (manager != null) {
try {
manager.onTimeZoneProviderUncertain();
} catch (RemoteException | RuntimeException e) {
Log.w(TAG, e);
}
}
});
}
/**
* Indicates there was a permanent failure. This is not generally expected, and probably means a
* required backend service has been turned down, or the client is unreasonably old.
*/
public final void reportPermanentFailure(@NonNull Throwable cause) {
Objects.requireNonNull(cause);
mHandler.post(() -> {
ITimeZoneProviderManager manager = mManager;
if (manager != null) {
try {
manager.onTimeZoneProviderPermanentFailure(cause.getMessage());
} catch (RemoteException | RuntimeException e) {
Log.w(TAG, e);
}
}
});
}
/**
* Starts the provider sending updates.
*/
public abstract void onStartUpdates(@DurationMillisLong long initializationTimeoutMillis);
/**
* Stops the provider sending updates.
*/
public abstract void onStopUpdates();
private class TimeZoneProviderServiceWrapper extends ITimeZoneProvider.Stub {
@Override
public void setTimeZoneProviderManager(ITimeZoneProviderManager manager) {
Objects.requireNonNull(manager);
mHandler.post(() -> TimeZoneProviderService.this.mManager = manager);
}
public void startUpdates(@DurationMillisLong long initializationTimeoutMillis) {
mHandler.post(() -> onStartUpdates(initializationTimeoutMillis));
}
public void stopUpdates() {
mHandler.post(TimeZoneProviderService.this::onStopUpdates);
}
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2020, 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.service.timezone;
/**
* @hide
*/
parcelable TimeZoneProviderSuggestion;

View File

@@ -0,0 +1,185 @@
/*
* Copyright (C) 2020 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.service.timezone;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* A suggestion from a {@link TimeZoneProviderService} containing zero or more time zones.
*
* @hide
*/
@SystemApi
public final class TimeZoneProviderSuggestion implements Parcelable {
@NonNull
private final List<String> mTimeZoneIds;
@ElapsedRealtimeLong
private final long mElapsedRealtimeMillis;
private TimeZoneProviderSuggestion(@NonNull List<String> timeZoneIds,
@ElapsedRealtimeLong long elapsedRealtimeMillis) {
mTimeZoneIds = immutableList(timeZoneIds);
mElapsedRealtimeMillis = elapsedRealtimeMillis;
}
/**
* Returns the time of the suggestion in elapsed real-time since system boot. Where possible,
* the time should be based on the time of the data used when determining time zone. For
* example, if it was based on a {@link android.location.Location} then it should be the time
* associated with that location.
*
* <p>This value is compared to {@link
* android.os.SystemClock#elapsedRealtime()}, to calculate the age of a fix and to compare
* {@link TimeZoneProviderSuggestion} instances.
*
* @return elapsed real-time of fix, in milliseconds
*/
@ElapsedRealtimeLong
public long getElapsedRealtimeMillis() {
return mElapsedRealtimeMillis;
}
/**
* Returns the zero or more time zone IDs for this suggestion.
*
* <p>Time zone IDs are TZDB IDs like "America/Los_Angeles" that would be accepted by {@link
* java.util.TimeZone#getTimeZone(String)}.
*
* <p>Most often a suggestion will contain a single time zone ID but other possibilities are
* valid. A suggestion with zero time zone IDs means the provider is certain there are no time
* zones for the current location, e.g. for oceans, boundaries or disputed areas. A suggestion
* with multiple IDs can occur on boundaries or disputed areas. The ordering should be in order
* of likelihood if possible, but the time zone detection service may choose from any of the
* zones suggested if it has other supporting information available.
*/
@NonNull
public List<String> getTimeZoneIds() {
return mTimeZoneIds;
}
@Override
public String toString() {
return "TimeZoneProviderSuggestion{"
+ "mTimeZoneIds=" + mTimeZoneIds
+ ", mElapsedRealtimeMillis=" + mElapsedRealtimeMillis
+ "(" + Duration.ofMillis(mElapsedRealtimeMillis) + ")"
+ '}';
}
public static final @NonNull Creator<TimeZoneProviderSuggestion> CREATOR =
new Creator<TimeZoneProviderSuggestion>() {
@Override
public TimeZoneProviderSuggestion createFromParcel(Parcel in) {
@SuppressWarnings("unchecked")
ArrayList<String> timeZoneIds =
(ArrayList<String>) in.readArrayList(null /* classLoader */);
long elapsedRealtimeMillis = in.readLong();
return new TimeZoneProviderSuggestion(timeZoneIds, elapsedRealtimeMillis);
}
@Override
public TimeZoneProviderSuggestion[] newArray(int size) {
return new TimeZoneProviderSuggestion[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeList(mTimeZoneIds);
parcel.writeLong(mElapsedRealtimeMillis);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TimeZoneProviderSuggestion that = (TimeZoneProviderSuggestion) o;
return mElapsedRealtimeMillis == that.mElapsedRealtimeMillis
&& mTimeZoneIds.equals(that.mTimeZoneIds);
}
@Override
public int hashCode() {
return Objects.hash(mTimeZoneIds, mElapsedRealtimeMillis);
}
/** A builder for {@link TimeZoneProviderSuggestion}. */
public static final class Builder {
private @NonNull List<String> mTimeZoneIds = Collections.emptyList();
@ElapsedRealtimeLong
private long mElapsedRealtimeMillis = SystemClock.elapsedRealtime();
/**
* Sets the time zone IDs of this suggestion.
*/
@NonNull
public Builder setTimeZoneIds(@NonNull List<String> timeZoneIds) {
mTimeZoneIds = Objects.requireNonNull(timeZoneIds);
return this;
}
/**
* Sets the time of this suggestion, in elapsed real-time since system boot.
*/
@NonNull
public Builder setElapsedRealtimeMillis(@ElapsedRealtimeLong long time) {
mElapsedRealtimeMillis = time;
return this;
}
/**
* Builds a {@link TimeZoneProviderSuggestion} instance.
*/
@NonNull
public TimeZoneProviderSuggestion build() {
return new TimeZoneProviderSuggestion(mTimeZoneIds, mElapsedRealtimeMillis);
}
}
@NonNull
private static List<String> immutableList(@NonNull List<String> list) {
Objects.requireNonNull(list);
if (list.isEmpty()) {
return Collections.emptyList();
} else {
return Collections.unmodifiableList(new ArrayList<>(list));
}
}
}

View File

@@ -5304,7 +5304,7 @@
<attribution android:tag="TwilightService" android:label="@string/twilight_service"/>
<!-- Attribution for the Offline LocationTimeZoneProvider, used to detect time zone using
on-device data -->
<attribution android:tag="OfflineLocationTimeZoneProvider"
<attribution android:tag="OfflineLocationTimeZoneProviderService"
android:label="@string/offline_location_time_zone_detection_service_attribution"/>
<application android:process="system"
@@ -5774,11 +5774,10 @@
<!-- AOSP configures a default secondary LocationTimeZoneProvider that uses an on-device
data set from the com.android.geotz APEX. -->
<uses-library android:name="com.android.location.provider" />
<service android:name="com.android.timezone.geotz.provider.OfflineLocationTimeZoneService"
<service android:name="com.android.timezone.geotz.provider.OfflineLocationTimeZoneProviderService"
android:exported="false">
<intent-filter>
<action android:name="com.android.location.timezone.service.v1.SecondaryLocationTimeZoneProvider" />
<action android:name="android.service.timezone.SecondaryLocationTimeZoneProviderService" />
</intent-filter>
<meta-data android:name="serviceVersion" android:value="1" />
<meta-data android:name="serviceIsMultiuser" android:value="true" />

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2019 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.service.timezone;
import static org.junit.Assert.assertEquals;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.reflect.Field;
/** Utility methods related to {@link Parcelable} objects used in several tests. */
final class ParcelableTestSupport {
private ParcelableTestSupport() {}
/** Returns the result of parceling and unparceling the argument. */
@SuppressWarnings("unchecked")
public static <T extends Parcelable> T roundTripParcelable(T parcelable) {
Parcel parcel = Parcel.obtain();
parcel.writeTypedObject(parcelable, 0);
parcel.setDataPosition(0);
Parcelable.Creator<T> creator;
try {
Field creatorField = parcelable.getClass().getField("CREATOR");
creator = (Parcelable.Creator<T>) creatorField.get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new AssertionError(e);
}
T toReturn = parcel.readTypedObject(creator);
parcel.recycle();
return toReturn;
}
public static <T extends Parcelable> void assertRoundTripParcelable(T instance) {
assertEquals(instance, roundTripParcelable(instance));
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2020 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.service.timezone;
import static android.service.timezone.ParcelableTestSupport.assertRoundTripParcelable;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static java.util.Collections.singletonList;
import org.junit.Test;
import java.util.List;
public class TimeZoneProviderSuggestionTest {
private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 9999;
private static final List<String> ARBITRARY_TIME_ZONE_IDS = singletonList("Europe/London");
@Test(expected = RuntimeException.class)
public void testInvalidTimeZoneIds() {
new TimeZoneProviderSuggestion.Builder()
.setTimeZoneIds(null);
}
@Test
public void testEquals() {
TimeZoneProviderSuggestion.Builder builder1 = new TimeZoneProviderSuggestion.Builder()
.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
{
TimeZoneProviderSuggestion one = builder1.build();
assertEquals(one, one);
}
TimeZoneProviderSuggestion.Builder builder2 = new TimeZoneProviderSuggestion.Builder()
.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
{
TimeZoneProviderSuggestion one = builder1.build();
TimeZoneProviderSuggestion two = builder2.build();
assertEquals(one, two);
assertEquals(two, one);
}
builder1.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS + 1);
{
TimeZoneProviderSuggestion one = builder1.build();
TimeZoneProviderSuggestion two = builder2.build();
assertNotEquals(one, two);
assertNotEquals(two, one);
}
builder2.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS + 1);
{
TimeZoneProviderSuggestion one = builder1.build();
TimeZoneProviderSuggestion two = builder2.build();
assertEquals(one, two);
assertEquals(two, one);
}
builder2.setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS);
{
TimeZoneProviderSuggestion one = builder1.build();
TimeZoneProviderSuggestion two = builder2.build();
assertNotEquals(one, two);
assertNotEquals(two, one);
}
builder1.setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS);
{
TimeZoneProviderSuggestion one = builder1.build();
TimeZoneProviderSuggestion two = builder2.build();
assertEquals(one, two);
assertEquals(two, one);
}
}
@Test
public void testParcelable_noTimeZoneIds() {
TimeZoneProviderSuggestion.Builder builder = new TimeZoneProviderSuggestion.Builder()
.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
assertRoundTripParcelable(builder.build());
}
@Test
public void testParcelable_withTimeZoneIds() {
TimeZoneProviderSuggestion.Builder builder = new TimeZoneProviderSuggestion.Builder()
.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS)
.setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS);
assertRoundTripParcelable(builder.build());
}
}

View File

@@ -17,20 +17,17 @@
package com.android.server.location.timezone;
import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_CERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_INITIALIZING;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_UNCERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.location.timezone.LocationTimeZoneEvent;
import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
import java.time.Duration;
import java.util.Objects;
@@ -57,9 +54,9 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
void onInitialize() {
mProxy.initialize(new LocationTimeZoneProviderProxy.Listener() {
@Override
public void onReportLocationTimeZoneEvent(
@NonNull LocationTimeZoneEvent locationTimeZoneEvent) {
handleLocationTimeZoneEvent(locationTimeZoneEvent);
public void onReportTimeZoneProviderEvent(
@NonNull TimeZoneProviderEvent timeZoneProviderEvent) {
handleTimeZoneProviderEvent(timeZoneProviderEvent);
}
@Override
@@ -80,27 +77,27 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
synchronized (mSharedLock) {
ProviderState currentState = mCurrentState.get();
switch (currentState.stateEnum) {
case PROVIDER_STATE_ENABLED_INITIALIZING:
case PROVIDER_STATE_ENABLED_UNCERTAIN:
case PROVIDER_STATE_ENABLED_CERTAIN: {
case PROVIDER_STATE_STARTED_INITIALIZING:
case PROVIDER_STATE_STARTED_UNCERTAIN:
case PROVIDER_STATE_STARTED_CERTAIN: {
// Losing a remote provider is treated as becoming uncertain.
String msg = "handleProviderLost reason=" + reason
+ ", mProviderName=" + mProviderName
+ ", currentState=" + currentState;
debugLog(msg);
// This is an unusual PROVIDER_STATE_ENABLED_UNCERTAIN state because
// This is an unusual PROVIDER_STATE_STARTED_UNCERTAIN state because
// event == null
ProviderState newState = currentState.newState(
PROVIDER_STATE_ENABLED_UNCERTAIN, null,
PROVIDER_STATE_STARTED_UNCERTAIN, null,
currentState.currentUserConfiguration, msg);
setCurrentState(newState, true);
break;
}
case PROVIDER_STATE_DISABLED: {
case PROVIDER_STATE_STOPPED: {
debugLog("handleProviderLost reason=" + reason
+ ", mProviderName=" + mProviderName
+ ", currentState=" + currentState
+ ": No state change required, provider is disabled.");
+ ": No state change required, provider is stopped.");
break;
}
case PROVIDER_STATE_PERM_FAILED: {
@@ -123,16 +120,16 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
synchronized (mSharedLock) {
ProviderState currentState = mCurrentState.get();
switch (currentState.stateEnum) {
case PROVIDER_STATE_ENABLED_INITIALIZING:
case PROVIDER_STATE_ENABLED_CERTAIN:
case PROVIDER_STATE_ENABLED_UNCERTAIN: {
case PROVIDER_STATE_STARTED_INITIALIZING:
case PROVIDER_STATE_STARTED_CERTAIN:
case PROVIDER_STATE_STARTED_UNCERTAIN: {
debugLog("handleOnProviderBound mProviderName=" + mProviderName
+ ", currentState=" + currentState + ": Provider is enabled.");
+ ", currentState=" + currentState + ": Provider is started.");
break;
}
case PROVIDER_STATE_DISABLED: {
case PROVIDER_STATE_STOPPED: {
debugLog("handleOnProviderBound mProviderName=" + mProviderName
+ ", currentState=" + currentState + ": Provider is disabled.");
+ ", currentState=" + currentState + ": Provider is stopped.");
break;
}
case PROVIDER_STATE_PERM_FAILED: {
@@ -150,23 +147,17 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
}
@Override
void onEnable(@NonNull Duration initializationTimeout) {
void onStartUpdates(@NonNull Duration initializationTimeout) {
// Set a request on the proxy - it will be sent immediately if the service is bound,
// or will be sent as soon as the service becomes bound.
LocationTimeZoneProviderRequest request =
new LocationTimeZoneProviderRequest.Builder()
.setReportLocationTimeZone(true)
.setInitializationTimeoutMillis(initializationTimeout.toMillis())
.build();
TimeZoneProviderRequest request =
TimeZoneProviderRequest.createStartUpdatesRequest(initializationTimeout);
mProxy.setRequest(request);
}
@Override
void onDisable() {
LocationTimeZoneProviderRequest request =
new LocationTimeZoneProviderRequest.Builder()
.setReportLocationTimeZone(false)
.build();
void onStopUpdates() {
TimeZoneProviderRequest request = TimeZoneProviderRequest.createStopUpdatesRequest();
mProxy.setRequest(request);
}

View File

@@ -16,24 +16,24 @@
package com.android.server.location.timezone;
import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE;
import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_CERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_INITIALIZING;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_UNCERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE;
import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION;
import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.location.timezone.LocationTimeZoneEvent;
import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
@@ -46,9 +46,9 @@ import java.util.Objects;
* A real implementation of {@link LocationTimeZoneProviderController} that supports a primary and a
* secondary {@link LocationTimeZoneProvider}.
*
* <p>The primary is used until it fails or becomes uncertain. The secondary will then be enabled.
* <p>The primary is used until it fails or becomes uncertain. The secondary will then be started.
* The controller will immediately make suggestions based on "certain" {@link
* LocationTimeZoneEvent}s, i.e. events that demonstrate the provider is certain what the time zone
* TimeZoneProviderEvent}s, i.e. events that demonstrate the provider is certain what the time zone
* is. The controller will not make immediate suggestions based on "uncertain" events, giving
* providers time to change their mind. This also gives the secondary provider time to initialize
* when the primary becomes uncertain.
@@ -107,7 +107,7 @@ class ControllerImpl extends LocationTimeZoneProviderController {
mPrimaryProvider.initialize(providerListener);
mSecondaryProvider.initialize(providerListener);
alterProvidersEnabledStateIfRequired(
alterProvidersStartedStateIfRequired(
null /* oldConfiguration */, mCurrentUserConfiguration);
}
}
@@ -125,15 +125,15 @@ class ControllerImpl extends LocationTimeZoneProviderController {
if (!newConfig.equals(oldConfig)) {
if (newConfig.getUserId() != oldConfig.getUserId()) {
// If the user changed, disable the providers if needed. They may be re-enabled
// If the user changed, stop the providers if needed. They may be re-started
// for the new user immediately afterwards if their settings allow.
debugLog("User changed. old=" + oldConfig.getUserId()
+ ", new=" + newConfig.getUserId() + ": Disabling providers");
disableProviders();
+ ", new=" + newConfig.getUserId() + ": Stopping providers");
stopProviders();
alterProvidersEnabledStateIfRequired(null /* oldConfiguration */, newConfig);
alterProvidersStartedStateIfRequired(null /* oldConfiguration */, newConfig);
} else {
alterProvidersEnabledStateIfRequired(oldConfig, newConfig);
alterProvidersStartedStateIfRequired(oldConfig, newConfig);
}
}
}
@@ -145,43 +145,44 @@ class ControllerImpl extends LocationTimeZoneProviderController {
}
@Override
@DurationMillisLong
long getUncertaintyTimeoutDelayMillis() {
return mUncertaintyTimeoutQueue.getQueuedDelayMillis();
}
@GuardedBy("mSharedLock")
private void disableProviders() {
disableProviderIfEnabled(mPrimaryProvider);
disableProviderIfEnabled(mSecondaryProvider);
private void stopProviders() {
stopProviderIfStarted(mPrimaryProvider);
stopProviderIfStarted(mSecondaryProvider);
// By definition, if both providers are disabled, the controller is uncertain.
// By definition, if both providers are stopped, the controller is uncertain.
cancelUncertaintyTimeout();
}
@GuardedBy("mSharedLock")
private void disableProviderIfEnabled(@NonNull LocationTimeZoneProvider provider) {
if (provider.getCurrentState().isEnabled()) {
disableProvider(provider);
private void stopProviderIfStarted(@NonNull LocationTimeZoneProvider provider) {
if (provider.getCurrentState().isStarted()) {
stopProvider(provider);
}
}
@GuardedBy("mSharedLock")
private void disableProvider(@NonNull LocationTimeZoneProvider provider) {
private void stopProvider(@NonNull LocationTimeZoneProvider provider) {
ProviderState providerState = provider.getCurrentState();
switch (providerState.stateEnum) {
case PROVIDER_STATE_DISABLED: {
debugLog("No need to disable " + provider + ": already disabled");
case PROVIDER_STATE_STOPPED: {
debugLog("No need to stop " + provider + ": already stopped");
break;
}
case PROVIDER_STATE_ENABLED_INITIALIZING:
case PROVIDER_STATE_ENABLED_CERTAIN:
case PROVIDER_STATE_ENABLED_UNCERTAIN: {
debugLog("Disabling " + provider);
provider.disable();
case PROVIDER_STATE_STARTED_INITIALIZING:
case PROVIDER_STATE_STARTED_CERTAIN:
case PROVIDER_STATE_STARTED_UNCERTAIN: {
debugLog("Stopping " + provider);
provider.stopUpdates();
break;
}
case PROVIDER_STATE_PERM_FAILED: {
debugLog("Unable to disable " + provider + ": it is perm failed");
debugLog("Unable to stop " + provider + ": it is perm failed");
break;
}
default: {
@@ -192,20 +193,20 @@ class ControllerImpl extends LocationTimeZoneProviderController {
}
/**
* Sets the providers into the correct enabled/disabled state for the {@code newConfiguration}
* Sets the providers into the correct started/stopped state for the {@code newConfiguration}
* and, if there is a provider state change, makes any suggestions required to inform the
* downstream time zone detection code.
*
* <p>This is a utility method that exists to avoid duplicated logic for the various cases when
* provider enabled / disabled state may need to be set or changed, e.g. during initialization
* provider started / stopped state may need to be set or changed, e.g. during initialization
* or when a new configuration has been received.
*/
@GuardedBy("mSharedLock")
private void alterProvidersEnabledStateIfRequired(
private void alterProvidersStartedStateIfRequired(
@Nullable ConfigurationInternal oldConfiguration,
@NonNull ConfigurationInternal newConfiguration) {
// Provider enabled / disabled states only need to be changed if geoDetectionEnabled has
// Provider started / stopped states only need to be changed if geoDetectionEnabled has
// changed.
boolean oldGeoDetectionEnabled = oldConfiguration != null
&& oldConfiguration.getGeoDetectionEnabledBehavior();
@@ -215,28 +216,28 @@ class ControllerImpl extends LocationTimeZoneProviderController {
}
// The check above ensures that the logic below only executes if providers are going from
// {enabled *} -> {disabled}, or {disabled} -> {enabled initializing}. If this changes in
// future and there could be {enabled *} -> {enabled *} cases, or cases where the provider
// can't be assumed to go straight to the {enabled initializing} state, then the logic below
// {started *} -> {stopped}, or {stopped} -> {started initializing}. If this changes in
// future and there could be {started *} -> {started *} cases, or cases where the provider
// can't be assumed to go straight to the {started initializing} state, then the logic below
// would need to cover extra conditions, for example:
// 1) If the primary is in {enabled uncertain}, the secondary should be enabled.
// 1) If the primary is in {started uncertain}, the secondary should be started.
// 2) If (1), and the secondary instantly enters the {perm failed} state, the uncertainty
// timeout started when the primary entered {enabled uncertain} should be cancelled.
// timeout started when the primary entered {started uncertain} should be cancelled.
if (newGeoDetectionEnabled) {
// Try to enable the primary provider.
tryEnableProvider(mPrimaryProvider, newConfiguration);
// Try to start the primary provider.
tryStartProvider(mPrimaryProvider, newConfiguration);
// The secondary should only ever be enabled if the primary now isn't enabled (i.e. it
// couldn't become {enabled initializing} because it is {perm failed}).
// The secondary should only ever be started if the primary now isn't started (i.e. it
// couldn't become {started initializing} because it is {perm failed}).
ProviderState newPrimaryState = mPrimaryProvider.getCurrentState();
if (!newPrimaryState.isEnabled()) {
// If the primary provider is {perm failed} then the controller must try to enable
if (!newPrimaryState.isStarted()) {
// If the primary provider is {perm failed} then the controller must try to start
// the secondary.
tryEnableProvider(mSecondaryProvider, newConfiguration);
tryStartProvider(mSecondaryProvider, newConfiguration);
ProviderState newSecondaryState = mSecondaryProvider.getCurrentState();
if (!newSecondaryState.isEnabled()) {
if (!newSecondaryState.isStarted()) {
// If both providers are {perm failed} then the controller immediately
// becomes uncertain.
GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
@@ -247,7 +248,7 @@ class ControllerImpl extends LocationTimeZoneProviderController {
}
}
} else {
disableProviders();
stopProviders();
// There can be an uncertainty timeout set if the controller most recently received
// an uncertain event. This is a no-op if there isn't a timeout set.
@@ -256,34 +257,35 @@ class ControllerImpl extends LocationTimeZoneProviderController {
// If a previous "certain" suggestion has been made, then a new "uncertain"
// suggestion must now be made to indicate the controller {does not / no longer has}
// an opinion and will not be sending further updates (until at least the config
// changes again and providers are re-enabled).
// changes again and providers are re-started).
if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
"Provider is disabled:"
"Provider is stopped:"
+ " primary=" + mPrimaryProvider.getCurrentState());
makeSuggestion(suggestion);
}
}
}
private void tryEnableProvider(@NonNull LocationTimeZoneProvider provider,
private void tryStartProvider(@NonNull LocationTimeZoneProvider provider,
@NonNull ConfigurationInternal configuration) {
ProviderState providerState = provider.getCurrentState();
switch (providerState.stateEnum) {
case PROVIDER_STATE_DISABLED: {
case PROVIDER_STATE_STOPPED: {
debugLog("Enabling " + provider);
provider.enable(configuration, mEnvironment.getProviderInitializationTimeout(),
provider.startUpdates(configuration,
mEnvironment.getProviderInitializationTimeout(),
mEnvironment.getProviderInitializationTimeoutFuzz());
break;
}
case PROVIDER_STATE_ENABLED_INITIALIZING:
case PROVIDER_STATE_ENABLED_CERTAIN:
case PROVIDER_STATE_ENABLED_UNCERTAIN: {
debugLog("No need to enable " + provider + ": already enabled");
case PROVIDER_STATE_STARTED_INITIALIZING:
case PROVIDER_STATE_STARTED_CERTAIN:
case PROVIDER_STATE_STARTED_UNCERTAIN: {
debugLog("No need to start " + provider + ": already started");
break;
}
case PROVIDER_STATE_PERM_FAILED: {
debugLog("Unable to enable " + provider + ": it is perm failed");
debugLog("Unable to start " + provider + ": it is perm failed");
break;
}
default: {
@@ -300,20 +302,20 @@ class ControllerImpl extends LocationTimeZoneProviderController {
synchronized (mSharedLock) {
switch (providerState.stateEnum) {
case PROVIDER_STATE_DISABLED: {
// This should never happen: entering disabled does not trigger a state change.
warnLog("onProviderStateChange: Unexpected state change for disabled provider,"
case PROVIDER_STATE_STOPPED: {
// This should never happen: entering stopped does not trigger a state change.
warnLog("onProviderStateChange: Unexpected state change for stopped provider,"
+ " provider=" + provider);
break;
}
case PROVIDER_STATE_ENABLED_INITIALIZING:
case PROVIDER_STATE_ENABLED_CERTAIN:
case PROVIDER_STATE_ENABLED_UNCERTAIN: {
// Entering enabled does not trigger a state change, so this only happens if an
// event is received while the provider is enabled.
case PROVIDER_STATE_STARTED_INITIALIZING:
case PROVIDER_STATE_STARTED_CERTAIN:
case PROVIDER_STATE_STARTED_UNCERTAIN: {
// Entering started does not trigger a state change, so this only happens if an
// event is received while the provider is started.
debugLog("onProviderStateChange: Received notification of a state change while"
+ " enabled, provider=" + provider);
handleProviderEnabledStateChange(providerState);
+ " started, provider=" + provider);
handleProviderStartedStateChange(providerState);
break;
}
case PROVIDER_STATE_PERM_FAILED: {
@@ -344,18 +346,18 @@ class ControllerImpl extends LocationTimeZoneProviderController {
ProviderState primaryCurrentState = mPrimaryProvider.getCurrentState();
ProviderState secondaryCurrentState = mSecondaryProvider.getCurrentState();
// If a provider has failed, the other may need to be enabled.
// If a provider has failed, the other may need to be started.
if (failedProvider == mPrimaryProvider) {
if (secondaryCurrentState.stateEnum != PROVIDER_STATE_PERM_FAILED) {
// The primary must have failed. Try to enable the secondary. This does nothing if
// the provider is already enabled, and will leave the provider in
// {enabled initializing} if the provider is disabled.
tryEnableProvider(mSecondaryProvider, mCurrentUserConfiguration);
// The primary must have failed. Try to start the secondary. This does nothing if
// the provider is already started, and will leave the provider in
// {started initializing} if the provider is stopped.
tryStartProvider(mSecondaryProvider, mCurrentUserConfiguration);
}
} else if (failedProvider == mSecondaryProvider) {
// No-op: The secondary will only be active if the primary is uncertain or is failed.
// So, there the primary should not need to be enabled when the secondary fails.
if (primaryCurrentState.stateEnum != PROVIDER_STATE_ENABLED_UNCERTAIN
// So, there the primary should not need to be started when the secondary fails.
if (primaryCurrentState.stateEnum != PROVIDER_STATE_STARTED_UNCERTAIN
&& primaryCurrentState.stateEnum != PROVIDER_STATE_PERM_FAILED) {
warnLog("Secondary provider unexpected reported a failure:"
+ " failed provider=" + failedProvider.getName()
@@ -384,16 +386,16 @@ class ControllerImpl extends LocationTimeZoneProviderController {
}
/**
* Called when a provider has changed state but just moved from one enabled state to another
* enabled state, usually as a result of a new {@link LocationTimeZoneEvent} being received.
* Called when a provider has changed state but just moved from one started state to another
* started state, usually as a result of a new {@link TimeZoneProviderEvent} being received.
* However, there are rare cases where the event can also be null.
*/
@GuardedBy("mSharedLock")
private void handleProviderEnabledStateChange(@NonNull ProviderState providerState) {
private void handleProviderStartedStateChange(@NonNull ProviderState providerState) {
LocationTimeZoneProvider provider = providerState.provider;
LocationTimeZoneEvent event = providerState.event;
TimeZoneProviderEvent event = providerState.event;
if (event == null) {
// Implicit uncertainty, i.e. where the provider is enabled, but a problem has been
// Implicit uncertainty, i.e. where the provider is started, but a problem has been
// detected without having received an event. For example, if the process has detected
// the loss of a binder-based provider, or initialization took too long. This is treated
// the same as explicit uncertainty, i.e. where the provider has explicitly told this
@@ -404,18 +406,18 @@ class ControllerImpl extends LocationTimeZoneProviderController {
}
if (!mCurrentUserConfiguration.getGeoDetectionEnabledBehavior()) {
// This should not happen: the provider should not be in an enabled state if the user
// This should not happen: the provider should not be in an started state if the user
// does not have geodetection enabled.
warnLog("Provider=" + provider + " is enabled, but"
warnLog("Provider=" + provider + " is started, but"
+ " currentUserConfiguration=" + mCurrentUserConfiguration
+ " suggests it shouldn't be.");
}
switch (event.getEventType()) {
switch (event.getType()) {
case EVENT_TYPE_PERMANENT_FAILURE: {
// This shouldn't happen. A provider cannot be enabled and have this event type.
// This shouldn't happen. A provider cannot be started and have this event type.
warnLog("Provider=" + provider
+ " is enabled, but event suggests it shouldn't be");
+ " is started, but event suggests it shouldn't be");
break;
}
case EVENT_TYPE_UNCERTAIN: {
@@ -423,13 +425,13 @@ class ControllerImpl extends LocationTimeZoneProviderController {
+ ", explicit uncertainty. event=" + event);
break;
}
case EVENT_TYPE_SUCCESS: {
handleProviderCertainty(provider, event.getTimeZoneIds(),
case EVENT_TYPE_SUGGESTION: {
handleProviderSuggestion(provider, event.getSuggestion().getTimeZoneIds(),
"Event received provider=" + provider + ", event=" + event);
break;
}
default: {
warnLog("Unknown eventType=" + event.getEventType());
warnLog("Unknown eventType=" + event.getType());
break;
}
}
@@ -439,7 +441,7 @@ class ControllerImpl extends LocationTimeZoneProviderController {
* Called when a provider has become "certain" about the time zone(s).
*/
@GuardedBy("mSharedLock")
private void handleProviderCertainty(
private void handleProviderSuggestion(
@NonNull LocationTimeZoneProvider provider,
@Nullable List<String> timeZoneIds,
@NonNull String reason) {
@@ -447,11 +449,10 @@ class ControllerImpl extends LocationTimeZoneProviderController {
cancelUncertaintyTimeout();
if (provider == mPrimaryProvider) {
disableProviderIfEnabled(mSecondaryProvider);
stopProviderIfStarted(mSecondaryProvider);
}
GeolocationTimeZoneSuggestion suggestion =
new GeolocationTimeZoneSuggestion(timeZoneIds);
GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(timeZoneIds);
suggestion.addDebugInfo(reason);
// Rely on the receiver to dedupe suggestions. It is better to over-communicate.
makeSuggestion(suggestion);
@@ -503,7 +504,7 @@ class ControllerImpl extends LocationTimeZoneProviderController {
* Called when a provider has become "uncertain" about the time zone.
*
* <p>A provider is expected to report its uncertainty as soon as it becomes uncertain, as
* this enables the most flexibility for the controller to enable other providers when there are
* this enables the most flexibility for the controller to start other providers when there are
* multiple ones available. The controller is therefore responsible for deciding when to make a
* "uncertain" suggestion to the downstream time zone detector.
*
@@ -532,11 +533,11 @@ class ControllerImpl extends LocationTimeZoneProviderController {
}
if (provider == mPrimaryProvider) {
// (Try to) enable the secondary. It could already be enabled, or enabling might not
// (Try to) start the secondary. It could already be started, or enabling might not
// succeed if the provider has previously reported it is perm failed. The uncertainty
// timeout (set above) is used to ensure that an uncertain suggestion will be made if
// the secondary cannot generate a success event in time.
tryEnableProvider(mSecondaryProvider, mCurrentUserConfiguration);
tryStartProvider(mSecondaryProvider, mCurrentUserConfiguration);
}
}

View File

@@ -16,6 +16,7 @@
package com.android.server.location.timezone;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.os.Handler;
@@ -57,12 +58,12 @@ final class HandlerThreadingDomain extends ThreadingDomain {
}
@Override
void postDelayed(@NonNull Runnable r, long delayMillis) {
void postDelayed(@NonNull Runnable r, @DurationMillisLong long delayMillis) {
getHandler().postDelayed(r, delayMillis);
}
@Override
void postDelayed(Runnable r, Object token, long delayMillis) {
void postDelayed(Runnable r, Object token, @DurationMillisLong long delayMillis) {
getHandler().postDelayed(r, token, delayMillis);
}

View File

@@ -24,6 +24,7 @@ import android.os.Binder;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.SystemProperties;
import android.service.timezone.TimeZoneProviderService;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
@@ -44,7 +45,7 @@ import java.util.Objects;
* A service class that acts as a container for the {@link LocationTimeZoneProviderController},
* which determines what {@link com.android.server.timezonedetector.GeolocationTimeZoneSuggestion}
* are made to the {@link TimeZoneDetectorInternal}, and the {@link LocationTimeZoneProvider}s that
* (indirectly) generate {@link com.android.internal.location.timezone.LocationTimeZoneEvent}s.
* (indirectly) generate {@link TimeZoneProviderEvent}s.
*
* <p>For details of the time zone suggestion behavior, see {@link
* LocationTimeZoneProviderController}.
@@ -120,9 +121,9 @@ public class LocationTimeZoneManagerService extends Binder {
private static final String ATTRIBUTION_TAG = "LocationTimeZoneService";
private static final String PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION =
"com.android.location.timezone.service.v1.PrimaryLocationTimeZoneProvider";
TimeZoneProviderService.PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE;
private static final String SECONDARY_LOCATION_TIME_ZONE_SERVICE_ACTION =
"com.android.location.timezone.service.v1.SecondaryLocationTimeZoneProvider";
TimeZoneProviderService.SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE;
@NonNull private final Context mContext;

View File

@@ -16,16 +16,17 @@
package com.android.server.location.timezone;
import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE;
import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_CERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_INITIALIZING;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_UNCERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE;
import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION;
import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -34,7 +35,6 @@ import android.os.SystemClock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.location.timezone.LocationTimeZoneEvent;
import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue;
import com.android.server.timezonedetector.ConfigurationInternal;
@@ -50,7 +50,7 @@ import java.util.Objects;
* process, or could be a stubbed instance when no real provider is registered.
*
* <p>The provider is supplied with a {@link ProviderListener} via {@link
* #initialize(ProviderListener)}. This enables it to communicates asynchronous detection / error
* #initialize(ProviderListener)}. This starts communication of asynchronous detection / error
* events back to the {@link LocationTimeZoneProviderController} via the {@link
* ProviderListener#onProviderStateChange} method. This call must be made on the
* {@link Handler} thread from the {@link ThreadingDomain} passed to the constructor.
@@ -77,9 +77,10 @@ abstract class LocationTimeZoneProvider implements Dumpable {
*/
static class ProviderState {
@IntDef({ PROVIDER_STATE_UNKNOWN, PROVIDER_STATE_ENABLED_INITIALIZING,
PROVIDER_STATE_ENABLED_CERTAIN, PROVIDER_STATE_ENABLED_UNCERTAIN,
PROVIDER_STATE_DISABLED, PROVIDER_STATE_PERM_FAILED })
@IntDef(prefix = "PROVIDER_STATE_",
value = { PROVIDER_STATE_UNKNOWN, PROVIDER_STATE_STARTED_INITIALIZING,
PROVIDER_STATE_STARTED_CERTAIN, PROVIDER_STATE_STARTED_UNCERTAIN,
PROVIDER_STATE_STOPPED, PROVIDER_STATE_PERM_FAILED })
@interface ProviderStateEnum {}
/**
@@ -88,31 +89,31 @@ abstract class LocationTimeZoneProvider implements Dumpable {
static final int PROVIDER_STATE_UNKNOWN = 0;
/**
* The provider is enabled and has not reported its first event.
* The provider is started and has not reported its first event.
*/
static final int PROVIDER_STATE_ENABLED_INITIALIZING = 1;
static final int PROVIDER_STATE_STARTED_INITIALIZING = 1;
/**
* The provider is enabled and most recently reported a "success" event.
* The provider is started and most recently reported a "suggestion" event.
*/
static final int PROVIDER_STATE_ENABLED_CERTAIN = 2;
static final int PROVIDER_STATE_STARTED_CERTAIN = 2;
/**
* The provider is enabled and most recently reported an "uncertain" event.
* The provider is started and most recently reported an "uncertain" event.
*/
static final int PROVIDER_STATE_ENABLED_UNCERTAIN = 3;
static final int PROVIDER_STATE_STARTED_UNCERTAIN = 3;
/**
* The provider is disabled.
* The provider is stopped.
*
* This is the state after {@link #initialize} is called.
*/
static final int PROVIDER_STATE_DISABLED = 4;
static final int PROVIDER_STATE_STOPPED = 4;
/**
* The provider has failed and cannot be re-enabled.
* The provider has failed and cannot be re-started.
*
* Providers may enter this state after a provider is enabled.
* Providers may enter this state after a provider is started.
*/
static final int PROVIDER_STATE_PERM_FAILED = 5;
@@ -123,16 +124,16 @@ abstract class LocationTimeZoneProvider implements Dumpable {
public final @ProviderStateEnum int stateEnum;
/**
* The last {@link LocationTimeZoneEvent} received. Only populated when {@link #stateEnum}
* is either {@link #PROVIDER_STATE_ENABLED_CERTAIN} or {@link
* #PROVIDER_STATE_ENABLED_UNCERTAIN}, but it can be {@code null} then too if no event has
* The last {@link TimeZoneProviderEvent} received. Only populated when {@link #stateEnum}
* is either {@link #PROVIDER_STATE_STARTED_CERTAIN} or {@link
* #PROVIDER_STATE_STARTED_UNCERTAIN}, but it can be {@code null} then too if no event has
* yet been received.
*/
@Nullable public final LocationTimeZoneEvent event;
@Nullable public final TimeZoneProviderEvent event;
/**
* The user configuration associated with the current state. Only and always present when
* {@link #stateEnum} is one of the enabled states.
* {@link #stateEnum} is one of the started states.
*/
@Nullable public final ConfigurationInternal currentUserConfiguration;
@@ -140,6 +141,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
* The time according to the elapsed realtime clock when the provider entered the current
* state. Included for debugging, not used for equality.
*/
@ElapsedRealtimeLong
private final long mStateEntryTimeMillis;
/**
@@ -151,7 +153,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
private ProviderState(@NonNull LocationTimeZoneProvider provider,
@ProviderStateEnum int stateEnum,
@Nullable LocationTimeZoneEvent event,
@Nullable TimeZoneProviderEvent event,
@Nullable ConfigurationInternal currentUserConfiguration,
@Nullable String debugInfo) {
this.provider = Objects.requireNonNull(provider);
@@ -174,25 +176,25 @@ abstract class LocationTimeZoneProvider implements Dumpable {
* and that the required parameters for the new state are present / absent.
*/
ProviderState newState(@ProviderStateEnum int newStateEnum,
@Nullable LocationTimeZoneEvent event,
@Nullable TimeZoneProviderEvent event,
@Nullable ConfigurationInternal currentUserConfig,
@Nullable String debugInfo) {
// Check valid "from" transitions.
switch (this.stateEnum) {
case PROVIDER_STATE_UNKNOWN: {
if (newStateEnum != PROVIDER_STATE_DISABLED) {
if (newStateEnum != PROVIDER_STATE_STOPPED) {
throw new IllegalArgumentException(
"Must transition from " + prettyPrintStateEnum(
PROVIDER_STATE_UNKNOWN)
+ " to " + prettyPrintStateEnum(PROVIDER_STATE_DISABLED));
+ " to " + prettyPrintStateEnum(PROVIDER_STATE_STOPPED));
}
break;
}
case PROVIDER_STATE_DISABLED:
case PROVIDER_STATE_ENABLED_INITIALIZING:
case PROVIDER_STATE_ENABLED_CERTAIN:
case PROVIDER_STATE_ENABLED_UNCERTAIN: {
case PROVIDER_STATE_STOPPED:
case PROVIDER_STATE_STARTED_INITIALIZING:
case PROVIDER_STATE_STARTED_CERTAIN:
case PROVIDER_STATE_STARTED_UNCERTAIN: {
// These can go to each other or PROVIDER_STATE_PERM_FAILED.
break;
}
@@ -211,21 +213,21 @@ abstract class LocationTimeZoneProvider implements Dumpable {
throw new IllegalArgumentException("Cannot transition to "
+ prettyPrintStateEnum(PROVIDER_STATE_UNKNOWN));
}
case PROVIDER_STATE_DISABLED: {
case PROVIDER_STATE_STOPPED: {
if (event != null || currentUserConfig != null) {
throw new IllegalArgumentException(
"Disabled state: event and currentUserConfig must be null"
"Stopped state: event and currentUserConfig must be null"
+ ", event=" + event
+ ", currentUserConfig=" + currentUserConfig);
}
break;
}
case PROVIDER_STATE_ENABLED_INITIALIZING:
case PROVIDER_STATE_ENABLED_CERTAIN:
case PROVIDER_STATE_ENABLED_UNCERTAIN: {
case PROVIDER_STATE_STARTED_INITIALIZING:
case PROVIDER_STATE_STARTED_CERTAIN:
case PROVIDER_STATE_STARTED_UNCERTAIN: {
if (currentUserConfig == null) {
throw new IllegalArgumentException(
"Enabled state: currentUserConfig must not be null");
"Started state: currentUserConfig must not be null");
}
break;
}
@@ -245,11 +247,11 @@ abstract class LocationTimeZoneProvider implements Dumpable {
return new ProviderState(provider, newStateEnum, event, currentUserConfig, debugInfo);
}
/** Returns {@code true} if {@link #stateEnum} is one of the enabled states. */
boolean isEnabled() {
return stateEnum == PROVIDER_STATE_ENABLED_INITIALIZING
|| stateEnum == PROVIDER_STATE_ENABLED_CERTAIN
|| stateEnum == PROVIDER_STATE_ENABLED_UNCERTAIN;
/** Returns {@code true} if {@link #stateEnum} is one of the started states. */
boolean isStarted() {
return stateEnum == PROVIDER_STATE_STARTED_INITIALIZING
|| stateEnum == PROVIDER_STATE_STARTED_CERTAIN
|| stateEnum == PROVIDER_STATE_STARTED_UNCERTAIN;
}
@Override
@@ -286,14 +288,14 @@ abstract class LocationTimeZoneProvider implements Dumpable {
private static String prettyPrintStateEnum(@ProviderStateEnum int state) {
switch (state) {
case PROVIDER_STATE_DISABLED:
return "Disabled (" + PROVIDER_STATE_DISABLED + ")";
case PROVIDER_STATE_ENABLED_INITIALIZING:
return "Enabled initializing (" + PROVIDER_STATE_ENABLED_INITIALIZING + ")";
case PROVIDER_STATE_ENABLED_CERTAIN:
return "Enabled certain (" + PROVIDER_STATE_ENABLED_CERTAIN + ")";
case PROVIDER_STATE_ENABLED_UNCERTAIN:
return "Enabled uncertain (" + PROVIDER_STATE_ENABLED_UNCERTAIN + ")";
case PROVIDER_STATE_STOPPED:
return "Stopped (" + PROVIDER_STATE_STOPPED + ")";
case PROVIDER_STATE_STARTED_INITIALIZING:
return "Started initializing (" + PROVIDER_STATE_STARTED_INITIALIZING + ")";
case PROVIDER_STATE_STARTED_CERTAIN:
return "Started certain (" + PROVIDER_STATE_STARTED_CERTAIN + ")";
case PROVIDER_STATE_STARTED_UNCERTAIN:
return "Started uncertain (" + PROVIDER_STATE_STARTED_UNCERTAIN + ")";
case PROVIDER_STATE_PERM_FAILED:
return "Perm failure (" + PROVIDER_STATE_PERM_FAILED + ")";
case PROVIDER_STATE_UNKNOWN:
@@ -315,7 +317,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
new ReferenceWithHistory<>(10);
/**
* Used for scheduling initialization timeouts, i.e. for providers that have just been enabled.
* Used for scheduling initialization timeouts, i.e. for providers that have just been started.
*/
@NonNull private final SingleRunnableQueue mInitializationTimeoutQueue;
@@ -344,7 +346,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
mProviderListener = Objects.requireNonNull(providerListener);
ProviderState currentState = ProviderState.createStartingState(this);
ProviderState newState = currentState.newState(
PROVIDER_STATE_DISABLED, null, null,
PROVIDER_STATE_STOPPED, null, null,
"initialize() called");
setCurrentState(newState, false);
@@ -407,28 +409,28 @@ abstract class LocationTimeZoneProvider implements Dumpable {
}
/**
* Enables the provider. It is an error to call this method except when the {@link
* #getCurrentState()} is at {@link ProviderState#PROVIDER_STATE_DISABLED}. This method must be
* Starts the provider. It is an error to call this method except when the {@link
* #getCurrentState()} is at {@link ProviderState#PROVIDER_STATE_STOPPED}. This method must be
* called using the handler thread from the {@link ThreadingDomain}.
*/
final void enable(@NonNull ConfigurationInternal currentUserConfiguration,
final void startUpdates(@NonNull ConfigurationInternal currentUserConfiguration,
@NonNull Duration initializationTimeout, @NonNull Duration initializationTimeoutFuzz) {
mThreadingDomain.assertCurrentThread();
synchronized (mSharedLock) {
assertCurrentState(PROVIDER_STATE_DISABLED);
assertCurrentState(PROVIDER_STATE_STOPPED);
ProviderState currentState = mCurrentState.get();
ProviderState newState = currentState.newState(
PROVIDER_STATE_ENABLED_INITIALIZING, null /* event */,
currentUserConfiguration, "enable() called");
PROVIDER_STATE_STARTED_INITIALIZING, null /* event */,
currentUserConfiguration, "startUpdates() called");
setCurrentState(newState, false);
Duration delay = initializationTimeout.plus(initializationTimeoutFuzz);
mInitializationTimeoutQueue.runDelayed(
this::handleInitializationTimeout, delay.toMillis());
onEnable(initializationTimeout);
onStartUpdates(initializationTimeout);
}
}
@@ -437,10 +439,10 @@ abstract class LocationTimeZoneProvider implements Dumpable {
synchronized (mSharedLock) {
ProviderState currentState = mCurrentState.get();
if (currentState.stateEnum == PROVIDER_STATE_ENABLED_INITIALIZING) {
if (currentState.stateEnum == PROVIDER_STATE_STARTED_INITIALIZING) {
// On initialization timeout the provider becomes uncertain.
ProviderState newState = currentState.newState(
PROVIDER_STATE_ENABLED_UNCERTAIN, null /* event */,
PROVIDER_STATE_STARTED_UNCERTAIN, null /* event */,
currentState.currentUserConfiguration, "initialization timeout");
setCurrentState(newState, true);
}
@@ -448,65 +450,64 @@ abstract class LocationTimeZoneProvider implements Dumpable {
}
/**
* Implemented by subclasses to do work during {@link #enable}.
* Implemented by subclasses to do work during {@link #startUpdates}.
*/
abstract void onEnable(@NonNull Duration initializationTimeout);
abstract void onStartUpdates(@NonNull Duration initializationTimeout);
/**
* Disables the provider. It is an error* to call this method except when the {@link
* #getCurrentState()} is one of the enabled states. This method must be
* Stops the provider. It is an error* to call this method except when the {@link
* #getCurrentState()} is one of the started states. This method must be
* called using the handler thread from the {@link ThreadingDomain}.
*/
final void disable() {
final void stopUpdates() {
mThreadingDomain.assertCurrentThread();
synchronized (mSharedLock) {
assertIsEnabled();
assertIsStarted();
ProviderState currentState = mCurrentState.get();
ProviderState newState = currentState.newState(
PROVIDER_STATE_DISABLED, null, null, "disable() called");
PROVIDER_STATE_STOPPED, null, null, "stopUpdates() called");
setCurrentState(newState, false);
if (mInitializationTimeoutQueue.hasQueued()) {
mInitializationTimeoutQueue.cancel();
}
onDisable();
onStopUpdates();
}
}
/**
* Implemented by subclasses to do work during {@link #disable}.
* Implemented by subclasses to do work during {@link #stopUpdates}.
*/
abstract void onDisable();
abstract void onStopUpdates();
/** For subclasses to invoke when a {@link LocationTimeZoneEvent} has been received. */
final void handleLocationTimeZoneEvent(
@NonNull LocationTimeZoneEvent locationTimeZoneEvent) {
/** For subclasses to invoke when a {@link TimeZoneProviderEvent} has been received. */
final void handleTimeZoneProviderEvent(@NonNull TimeZoneProviderEvent timeZoneProviderEvent) {
mThreadingDomain.assertCurrentThread();
Objects.requireNonNull(locationTimeZoneEvent);
Objects.requireNonNull(timeZoneProviderEvent);
synchronized (mSharedLock) {
debugLog("handleLocationTimeZoneEvent: mProviderName=" + mProviderName
+ ", locationTimeZoneEvent=" + locationTimeZoneEvent);
debugLog("handleTimeZoneProviderEvent: mProviderName=" + mProviderName
+ ", timeZoneProviderEvent=" + timeZoneProviderEvent);
ProviderState currentState = mCurrentState.get();
int eventType = locationTimeZoneEvent.getEventType();
int eventType = timeZoneProviderEvent.getType();
switch (currentState.stateEnum) {
case PROVIDER_STATE_PERM_FAILED: {
// After entering perm failed, there is nothing to do. The remote peer is
// supposed to stop sending events after it has reported perm failure.
logWarn("handleLocationTimeZoneEvent: Event=" + locationTimeZoneEvent
logWarn("handleTimeZoneProviderEvent: Event=" + timeZoneProviderEvent
+ " received for provider=" + this + " when in failed state");
return;
}
case PROVIDER_STATE_DISABLED: {
case PROVIDER_STATE_STOPPED: {
switch (eventType) {
case EVENT_TYPE_PERMANENT_FAILURE: {
String msg = "handleLocationTimeZoneEvent:"
+ " Failure event=" + locationTimeZoneEvent
+ " received for disabled provider=" + this
String msg = "handleTimeZoneProviderEvent:"
+ " Failure event=" + timeZoneProviderEvent
+ " received for stopped provider=" + this
+ ", entering permanently failed state";
logWarn(msg);
ProviderState newState = currentState.newState(
@@ -517,30 +518,30 @@ abstract class LocationTimeZoneProvider implements Dumpable {
}
return;
}
case EVENT_TYPE_SUCCESS:
case EVENT_TYPE_SUGGESTION:
case EVENT_TYPE_UNCERTAIN: {
// Any geolocation-related events received for a disabled provider are
// Any geolocation-related events received for a stopped provider are
// ignored: they should not happen.
logWarn("handleLocationTimeZoneEvent:"
+ " event=" + locationTimeZoneEvent
+ " received for disabled provider=" + this
logWarn("handleTimeZoneProviderEvent:"
+ " event=" + timeZoneProviderEvent
+ " received for stopped provider=" + this
+ ", ignoring");
return;
}
default: {
throw new IllegalStateException(
"Unknown eventType=" + locationTimeZoneEvent);
"Unknown eventType=" + timeZoneProviderEvent);
}
}
}
case PROVIDER_STATE_ENABLED_INITIALIZING:
case PROVIDER_STATE_ENABLED_CERTAIN:
case PROVIDER_STATE_ENABLED_UNCERTAIN: {
case PROVIDER_STATE_STARTED_INITIALIZING:
case PROVIDER_STATE_STARTED_CERTAIN:
case PROVIDER_STATE_STARTED_UNCERTAIN: {
switch (eventType) {
case EVENT_TYPE_PERMANENT_FAILURE: {
String msg = "handleLocationTimeZoneEvent:"
+ " Failure event=" + locationTimeZoneEvent
String msg = "handleTimeZoneProviderEvent:"
+ " Failure event=" + timeZoneProviderEvent
+ " received for provider=" + this
+ ", entering permanently failed state";
logWarn(msg);
@@ -554,16 +555,16 @@ abstract class LocationTimeZoneProvider implements Dumpable {
return;
}
case EVENT_TYPE_UNCERTAIN:
case EVENT_TYPE_SUCCESS: {
case EVENT_TYPE_SUGGESTION: {
@ProviderStateEnum int providerStateEnum;
if (eventType == EVENT_TYPE_UNCERTAIN) {
providerStateEnum = PROVIDER_STATE_ENABLED_UNCERTAIN;
providerStateEnum = PROVIDER_STATE_STARTED_UNCERTAIN;
} else {
providerStateEnum = PROVIDER_STATE_ENABLED_CERTAIN;
providerStateEnum = PROVIDER_STATE_STARTED_CERTAIN;
}
ProviderState newState = currentState.newState(providerStateEnum,
locationTimeZoneEvent, currentState.currentUserConfiguration,
"handleLocationTimeZoneEvent() when enabled");
timeZoneProviderEvent, currentState.currentUserConfiguration,
"handleTimeZoneProviderEvent() when started");
setCurrentState(newState, true);
if (mInitializationTimeoutQueue.hasQueued()) {
mInitializationTimeoutQueue.cancel();
@@ -572,7 +573,7 @@ abstract class LocationTimeZoneProvider implements Dumpable {
}
default: {
throw new IllegalStateException(
"Unknown eventType=" + locationTimeZoneEvent);
"Unknown eventType=" + timeZoneProviderEvent);
}
}
}
@@ -589,10 +590,10 @@ abstract class LocationTimeZoneProvider implements Dumpable {
abstract void logWarn(String msg);
@GuardedBy("mSharedLock")
private void assertIsEnabled() {
private void assertIsStarted() {
ProviderState currentState = mCurrentState.get();
if (!currentState.isEnabled()) {
throw new IllegalStateException("Required an enabled state, but was " + currentState);
if (!currentState.isStarted()) {
throw new IllegalStateException("Required a started state, but was " + currentState);
}
}

View File

@@ -16,6 +16,7 @@
package com.android.server.location.timezone;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.os.Handler;
@@ -59,7 +60,7 @@ import java.util.Objects;
* disputed areas and oceans. Distinguishing uncertainty allows the controller to try other
* providers (or give up), where as certainty means it should not.
*
* <p>A provider can fail permanently. A permanent failure will disable the provider until next
* <p>A provider can fail permanently. A permanent failure will stop the provider until next
* boot.
*/
abstract class LocationTimeZoneProviderController implements Dumpable {
@@ -90,6 +91,7 @@ abstract class LocationTimeZoneProviderController implements Dumpable {
abstract boolean isUncertaintyTimeoutSet();
@VisibleForTesting
@DurationMillisLong
abstract long getUncertaintyTimeoutDelayMillis();
/**

View File

@@ -23,17 +23,15 @@ import android.os.Handler;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.location.timezone.LocationTimeZoneEvent;
import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
import com.android.server.timezonedetector.Dumpable;
import java.util.Objects;
/**
* System server-side proxy for ILocationTimeZoneProvider implementations, i.e. this provides the
* system server object used to communicate with a remote LocationTimeZoneProvider over Binder,
* which could be running in a different process. As LocationTimeZoneProviders are bound / unbound
* this proxy will rebind to the "best" available remote process.
* System server-side proxy for ITimeZoneProvider implementations, i.e. this provides the system
* server object used to communicate with a remote TimeZoneProvider over Binder, which could be
* running in a different process. As TimeZoneProviders are bound / unbound this proxy will rebind
* to the "best" available remote process.
*
* <p>Threading guarantees provided / required by this interface:
* <ul>
@@ -41,8 +39,8 @@ import java.util.Objects;
* from the {@link ThreadingDomain} passed to the constructor, excluding
* {@link #dump(IndentingPrintWriter, String[])}</li>
* <li>Non-static public methods that make binder calls to remote processes (e.g.
* {@link #setRequest(LocationTimeZoneProviderRequest)}) are executed asynchronously and will
* return immediately.</li>
* {@link #setRequest(TimeZoneProviderRequest)}) are executed asynchronously and will return
* immediately.</li>
* <li>Callbacks received via binder are delivered via {@link Listener} are delivered on the
* {@link Handler} thread from the {@link ThreadingDomain} passed to the constructor.
* </ul>
@@ -93,16 +91,15 @@ abstract class LocationTimeZoneProviderProxy implements Dumpable {
/**
* Sets a new request for the provider.
*/
abstract void setRequest(@NonNull LocationTimeZoneProviderRequest request);
abstract void setRequest(@NonNull TimeZoneProviderRequest request);
/**
* Handles a {@link LocationTimeZoneEvent} from a remote process.
* Handles a {@link TimeZoneProviderEvent} from a remote process.
*/
final void handleLocationTimeZoneEvent(
@NonNull LocationTimeZoneEvent locationTimeZoneEvent) {
final void handleTimeZoneProviderEvent(@NonNull TimeZoneProviderEvent timeZoneProviderEvent) {
// These calls are invoked on a binder thread. Move to the mThreadingDomain thread as
// required by the guarantees for this class.
mThreadingDomain.post(() -> mListener.onReportLocationTimeZoneEvent(locationTimeZoneEvent));
mThreadingDomain.post(() -> mListener.onReportTimeZoneProviderEvent(timeZoneProviderEvent));
}
/**
@@ -112,9 +109,9 @@ abstract class LocationTimeZoneProviderProxy implements Dumpable {
interface Listener {
/**
* Called when a provider receives a {@link LocationTimeZoneEvent}.
* Called when a provider receives a {@link TimeZoneProviderEvent}.
*/
void onReportLocationTimeZoneEvent(@NonNull LocationTimeZoneEvent locationTimeZoneEvent);
void onReportTimeZoneProviderEvent(@NonNull TimeZoneProviderEvent timeZoneProviderEvent);
/**
* Called when a provider is (re)bound.

View File

@@ -35,11 +35,11 @@ import java.time.Duration;
* and a secondary {@link LocationTimeZoneProvider}, but only a primary is configured, the secondary
* config will be left null and the {@link LocationTimeZoneProvider} implementation will be
* defaulted to a {@link NullLocationTimeZoneProvider}. The {@link NullLocationTimeZoneProvider}
* enters a {@link ProviderState#PROVIDER_STATE_PERM_FAILED} state immediately after being enabled
* enters a {@link ProviderState#PROVIDER_STATE_PERM_FAILED} state immediately after being started
* for the first time and sends the appropriate event, which ensures the {@link
* LocationTimeZoneProviderController} won't expect any further {@link
* com.android.internal.location.timezone.LocationTimeZoneEvent}s to come from it, and won't attempt
* to use it again.
* TimeZoneProviderEvent}s to come from it, and won't attempt to use it
* again.
*/
class NullLocationTimeZoneProvider extends LocationTimeZoneProvider {
@@ -57,7 +57,7 @@ class NullLocationTimeZoneProvider extends LocationTimeZoneProvider {
}
@Override
void onEnable(@NonNull Duration initializationTimeout) {
void onStartUpdates(@NonNull Duration initializationTimeout) {
// Report a failure (asynchronously using the mThreadingDomain thread to avoid recursion).
mThreadingDomain.post(()-> {
// Enter the perm-failed state.
@@ -69,8 +69,8 @@ class NullLocationTimeZoneProvider extends LocationTimeZoneProvider {
}
@Override
void onDisable() {
// Ignored - NullLocationTimeZoneProvider is always permanently failed.
void onStopUpdates() {
// Ignored - this implementation is always permanently failed.
}
@Override

View File

@@ -22,22 +22,22 @@ import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import android.service.timezone.ITimeZoneProvider;
import android.service.timezone.ITimeZoneProviderManager;
import android.service.timezone.TimeZoneProviderSuggestion;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.location.timezone.ILocationTimeZoneProvider;
import com.android.internal.location.timezone.ILocationTimeZoneProviderManager;
import com.android.internal.location.timezone.LocationTimeZoneEvent;
import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
import com.android.server.ServiceWatcher;
import java.util.Objects;
/**
* System server-side proxy for ILocationTimeZoneProvider implementations, i.e. this provides the
* system server object used to communicate with a remote LocationTimeZoneProvider over Binder,
* which could be running in a different process. As "remote" LocationTimeZoneProviders are bound /
* unbound this proxy will rebind to the "best" available remote process.
* System server-side proxy for ITimeZoneProvider implementations, i.e. this provides the
* system server object used to communicate with a remote {@link
* android.service.timezone.TimeZoneProviderService} over Binder, which could be running in a
* different process. As "remote" providers are bound / unbound this proxy will rebind to the "best"
* available remote process.
*/
class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
@@ -47,7 +47,7 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
@Nullable private ManagerProxy mManagerProxy;
@GuardedBy("mProxyLock")
@NonNull private LocationTimeZoneProviderRequest mRequest;
@NonNull private TimeZoneProviderRequest mRequest;
RealLocationTimeZoneProviderProxy(
@NonNull Context context, @NonNull ThreadingDomain threadingDomain,
@@ -55,7 +55,7 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
int nonOverlayPackageResId) {
super(context, threadingDomain);
mManagerProxy = null;
mRequest = LocationTimeZoneProviderRequest.EMPTY_REQUEST;
mRequest = TimeZoneProviderRequest.createStopUpdatesRequest();
mServiceWatcher = new ServiceWatcher(context, action, this::onBind, this::onUnbind,
enableOverlayResId, nonOverlayPackageResId);
}
@@ -93,12 +93,12 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
private void onBindOnHandlerThread(@NonNull IBinder binder) {
mThreadingDomain.assertCurrentThread();
ILocationTimeZoneProvider provider = ILocationTimeZoneProvider.Stub.asInterface(binder);
ITimeZoneProvider provider = ITimeZoneProvider.Stub.asInterface(binder);
synchronized (mSharedLock) {
try {
mManagerProxy = new ManagerProxy();
provider.setLocationTimeZoneProviderManager(mManagerProxy);
provider.setTimeZoneProviderManager(mManagerProxy);
trySendCurrentRequest();
mListener.onProviderBound();
} catch (RemoteException e) {
@@ -118,7 +118,7 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
}
@Override
final void setRequest(@NonNull LocationTimeZoneProviderRequest request) {
final void setRequest(@NonNull TimeZoneProviderRequest request) {
mThreadingDomain.assertCurrentThread();
Objects.requireNonNull(request);
@@ -131,11 +131,14 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
@GuardedBy("mProxyLock")
private void trySendCurrentRequest() {
LocationTimeZoneProviderRequest request = mRequest;
TimeZoneProviderRequest request = mRequest;
mServiceWatcher.runOnBinder(binder -> {
ILocationTimeZoneProvider service =
ILocationTimeZoneProvider.Stub.asInterface(binder);
service.setRequest(request);
ITimeZoneProvider service = ITimeZoneProvider.Stub.asInterface(binder);
if (request.sendUpdates()) {
service.startUpdates(request.getInitializationTimeout().toMillis());
} else {
service.stopUpdates();
}
});
}
@@ -148,21 +151,39 @@ class RealLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
}
/**
* A system Server-side proxy for the ILocationTimeZoneProviderManager, i.e. this is a local
* binder stub. Each "remote" LocationTimeZoneProvider is passed a binder instance that it
* then uses to communicate back with the system server, invoking the logic here.
* A system Server-side proxy for the ITimeZoneProviderManager, i.e. this is a local binder stub
* Each "remote" TimeZoneProvider is passed a binder instance that it then uses to communicate
* back with the system server, invoking the logic here.
*/
private class ManagerProxy extends ILocationTimeZoneProviderManager.Stub {
private class ManagerProxy extends ITimeZoneProviderManager.Stub {
// executed on binder thread
@Override
public void onLocationTimeZoneEvent(LocationTimeZoneEvent locationTimeZoneEvent) {
public void onTimeZoneProviderSuggestion(TimeZoneProviderSuggestion suggestion) {
onTimeZoneProviderEvent(TimeZoneProviderEvent.createSuggestionEvent(suggestion));
}
// executed on binder thread
@Override
public void onTimeZoneProviderUncertain() {
onTimeZoneProviderEvent(TimeZoneProviderEvent.createUncertainEvent());
}
// executed on binder thread
@Override
public void onTimeZoneProviderPermanentFailure(String failureReason) {
onTimeZoneProviderEvent(
TimeZoneProviderEvent.createPermanentFailureEvent(failureReason));
}
private void onTimeZoneProviderEvent(TimeZoneProviderEvent event) {
synchronized (mSharedLock) {
if (mManagerProxy != this) {
return;
}
}
handleLocationTimeZoneEvent(locationTimeZoneEvent);
handleTimeZoneProviderEvent(event);
}
}
}

View File

@@ -16,9 +16,6 @@
package com.android.server.location.timezone;
import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE;
import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneManagerService.PRIMARY_PROVIDER_NAME;
import static com.android.server.location.timezone.LocationTimeZoneManagerService.SECONDARY_PROVIDER_NAME;
@@ -26,8 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.ShellCommand;
import android.os.SystemClock;
import com.android.internal.location.timezone.LocationTimeZoneEvent;
import android.service.timezone.TimeZoneProviderSuggestion;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -50,14 +46,14 @@ final class SimulatedBinderProviderEvent {
@NonNull private final String mProviderName;
private final int mEventType;
@Nullable private final LocationTimeZoneEvent mLocationTimeZoneEvent;
private final int mType;
@Nullable private final TimeZoneProviderEvent mTimeZoneProviderEvent;
private SimulatedBinderProviderEvent(@NonNull String providerName, int eventType,
@Nullable LocationTimeZoneEvent locationTimeZoneEvent) {
@Nullable TimeZoneProviderEvent timeZoneProviderEvent) {
this.mProviderName = Objects.requireNonNull(providerName);
this.mEventType = eventType;
this.mLocationTimeZoneEvent = locationTimeZoneEvent;
this.mType = eventType;
this.mTimeZoneProviderEvent = timeZoneProviderEvent;
}
@NonNull
@@ -66,24 +62,24 @@ final class SimulatedBinderProviderEvent {
}
@Nullable
LocationTimeZoneEvent getLocationTimeZoneEvent() {
return mLocationTimeZoneEvent;
TimeZoneProviderEvent getTimeZoneProviderEvent() {
return mTimeZoneProviderEvent;
}
int getEventType() {
return mEventType;
int getType() {
return mType;
}
/** Prints the command line options that {@link #createFromArgs(ShellCommand)} understands. */
static void printCommandLineOpts(PrintWriter pw) {
pw.println("Simulated provider binder event:");
pw.println();
pw.println("<provider name> [onBind|onUnbind|locationTimeZoneEvent"
pw.println("<provider name> [onBind|onUnbind|timeZoneProviderEvent"
+ " <location time zone event args>]");
pw.println();
pw.println("<provider name> = " + VALID_PROVIDER_NAMES);
pw.println("<location time zone event args> ="
+ " [PERMANENT_FAILURE|UNCERTAIN|SUCCESS <time zone ids>*]");
pw.println("<time zone provider event args> ="
+ " [PERMANENT_FAILURE|UNCERTAIN|SUGGESTION <time zone ids>*]");
}
/**
@@ -104,8 +100,8 @@ final class SimulatedBinderProviderEvent {
return new SimulatedBinderProviderEvent(
providerName, INJECTED_EVENT_TYPE_ON_UNBIND, null);
}
case "locationTimeZoneEvent": {
LocationTimeZoneEvent event = parseLocationTimeZoneEventArgs(shellCommand);
case "timeZoneProviderEvent": {
TimeZoneProviderEvent event = parseTimeZoneProviderEventArgs(shellCommand);
return new SimulatedBinderProviderEvent(providerName,
INJECTED_EVENT_TYPE_LOCATION_TIME_ZONE_EVENT, event);
}
@@ -115,30 +111,31 @@ final class SimulatedBinderProviderEvent {
}
}
private static LocationTimeZoneEvent parseLocationTimeZoneEventArgs(ShellCommand shellCommand) {
LocationTimeZoneEvent.Builder eventBuilder = new LocationTimeZoneEvent.Builder()
.setElapsedRealtimeMillis(SystemClock.elapsedRealtime());
private static TimeZoneProviderEvent parseTimeZoneProviderEventArgs(ShellCommand shellCommand) {
TimeZoneProviderEvent event;
String eventTypeString = shellCommand.getNextArgRequired();
switch (eventTypeString.toUpperCase()) {
case "PERMANENT_FAILURE": {
eventBuilder.setEventType(EVENT_TYPE_PERMANENT_FAILURE);
event = TimeZoneProviderEvent.createPermanentFailureEvent("Simulated");
break;
}
case "UNCERTAIN": {
eventBuilder.setEventType(EVENT_TYPE_UNCERTAIN);
event = TimeZoneProviderEvent.createUncertainEvent();
break;
}
case "SUCCESS": {
eventBuilder.setEventType(EVENT_TYPE_SUCCESS)
.setTimeZoneIds(parseTimeZoneArgs(shellCommand));
case "SUGGESTION": {
TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
.setElapsedRealtimeMillis(SystemClock.elapsedRealtime())
.setTimeZoneIds(parseTimeZoneArgs(shellCommand))
.build();
event = TimeZoneProviderEvent.createSuggestionEvent(suggestion);
break;
}
default: {
throw new IllegalArgumentException("Error: Unknown eventType: " + eventTypeString);
}
}
return eventBuilder.build();
return event;
}
private static List<String> parseTimeZoneArgs(ShellCommand shellCommand) {
@@ -154,8 +151,8 @@ final class SimulatedBinderProviderEvent {
public String toString() {
return "SimulatedBinderProviderEvent{"
+ "mProviderName=" + mProviderName
+ ", mEventType=" + mEventType
+ ", mLocationTimeZoneEvent=" + mLocationTimeZoneEvent
+ ", mType=" + mType
+ ", mTimeZoneProviderEvent=" + mTimeZoneProviderEvent
+ '}';
}
}

View File

@@ -26,7 +26,6 @@ import android.content.Context;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
import com.android.server.timezonedetector.ReferenceWithHistory;
import java.util.Objects;
@@ -38,7 +37,7 @@ import java.util.Objects;
class SimulatedLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
@GuardedBy("mSharedLock")
@NonNull private LocationTimeZoneProviderRequest mRequest;
@NonNull private TimeZoneProviderRequest mRequest;
@GuardedBy("mSharedLock")
@NonNull private final ReferenceWithHistory<String> mLastEvent = new ReferenceWithHistory<>(50);
@@ -46,7 +45,7 @@ class SimulatedLocationTimeZoneProviderProxy extends LocationTimeZoneProviderPro
SimulatedLocationTimeZoneProviderProxy(
@NonNull Context context, @NonNull ThreadingDomain threadingDomain) {
super(context, threadingDomain);
mRequest = LocationTimeZoneProviderRequest.EMPTY_REQUEST;
mRequest = TimeZoneProviderRequest.createStopUpdatesRequest();
}
@Override
@@ -60,7 +59,7 @@ class SimulatedLocationTimeZoneProviderProxy extends LocationTimeZoneProviderPro
Objects.requireNonNull(event);
synchronized (mSharedLock) {
switch (event.getEventType()) {
switch (event.getType()) {
case INJECTED_EVENT_TYPE_ON_BIND: {
mLastEvent.set("Simulating onProviderBound(), event=" + event);
mThreadingDomain.post(this::onBindOnHandlerThread);
@@ -72,12 +71,12 @@ class SimulatedLocationTimeZoneProviderProxy extends LocationTimeZoneProviderPro
break;
}
case INJECTED_EVENT_TYPE_LOCATION_TIME_ZONE_EVENT: {
if (!mRequest.getReportLocationTimeZone()) {
if (!mRequest.sendUpdates()) {
mLastEvent.set("Test event=" + event + " is testing an invalid case:"
+ " reporting is off. mRequest=" + mRequest);
}
mLastEvent.set("Simulating LocationTimeZoneEvent, event=" + event);
handleLocationTimeZoneEvent(event.getLocationTimeZoneEvent());
mLastEvent.set("Simulating TimeZoneProviderResult, event=" + event);
handleTimeZoneProviderEvent(event.getTimeZoneProviderEvent());
break;
}
default: {
@@ -106,7 +105,7 @@ class SimulatedLocationTimeZoneProviderProxy extends LocationTimeZoneProviderPro
}
@Override
final void setRequest(@NonNull LocationTimeZoneProviderRequest request) {
final void setRequest(@NonNull TimeZoneProviderRequest request) {
mThreadingDomain.assertCurrentThread();
Objects.requireNonNull(request);

View File

@@ -16,6 +16,7 @@
package com.android.server.location.timezone;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import com.android.internal.util.Preconditions;
@@ -68,9 +69,9 @@ abstract class ThreadingDomain {
/**
* Execute the supplied runnable on the threading domain's thread with a delay.
*/
abstract void postDelayed(@NonNull Runnable runnable, long delayMillis);
abstract void postDelayed(@NonNull Runnable runnable, @DurationMillisLong long delayMillis);
abstract void postDelayed(Runnable r, Object token, long delayMillis);
abstract void postDelayed(Runnable r, Object token, @DurationMillisLong long delayMillis);
abstract void removeQueuedRunnables(Object token);
@@ -90,6 +91,7 @@ abstract class ThreadingDomain {
final class SingleRunnableQueue {
private boolean mIsQueued;
@DurationMillisLong
private long mDelayMillis;
/**
@@ -97,7 +99,7 @@ abstract class ThreadingDomain {
* handler thread, cancelling any queued but not-yet-executed {@link Runnable} previously
* added by this. This method must be called from the threading domain's thread.
*/
void runDelayed(Runnable r, long delayMillis) {
void runDelayed(Runnable r, @DurationMillisLong long delayMillis) {
cancel();
mIsQueued = true;
mDelayMillis = delayMillis;
@@ -122,6 +124,7 @@ abstract class ThreadingDomain {
* IllegalStateException} if nothing is currently queued, see {@link #hasQueued()}.
* This method must be called from the threading domain's thread.
*/
@DurationMillisLong
long getQueuedDelayMillis() {
assertCurrentThread();
if (!mIsQueued) {

View File

@@ -0,0 +1,143 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.location.timezone;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.service.timezone.TimeZoneProviderService;
import android.service.timezone.TimeZoneProviderSuggestion;
import java.util.Objects;
/**
* An event from a {@link TimeZoneProviderService}.
*/
final class TimeZoneProviderEvent {
@IntDef(prefix = "EVENT_TYPE_",
value = { EVENT_TYPE_PERMANENT_FAILURE, EVENT_TYPE_SUGGESTION, EVENT_TYPE_UNCERTAIN })
public @interface EventType {}
/**
* The provider failed permanently. See {@link
* TimeZoneProviderService#reportPermanentFailure(Throwable)}
*/
public static final int EVENT_TYPE_PERMANENT_FAILURE = 1;
/**
* The provider made a suggestion. See {@link
* TimeZoneProviderService#reportSuggestion(TimeZoneProviderSuggestion)}
*/
public static final int EVENT_TYPE_SUGGESTION = 2;
/**
* The provider was uncertain about the time zone. See {@link
* TimeZoneProviderService#reportUncertain()}
*/
public static final int EVENT_TYPE_UNCERTAIN = 3;
private static final TimeZoneProviderEvent UNCERTAIN_EVENT =
new TimeZoneProviderEvent(EVENT_TYPE_UNCERTAIN, null, null);
@EventType
private final int mType;
@Nullable
private final TimeZoneProviderSuggestion mSuggestion;
@Nullable
private final String mFailureCause;
private TimeZoneProviderEvent(@EventType int type,
@Nullable TimeZoneProviderSuggestion suggestion,
@Nullable String failureCause) {
mType = type;
mSuggestion = suggestion;
mFailureCause = failureCause;
}
/** Returns a event of type {@link #EVENT_TYPE_SUGGESTION}. */
public static TimeZoneProviderEvent createSuggestionEvent(
@NonNull TimeZoneProviderSuggestion suggestion) {
return new TimeZoneProviderEvent(EVENT_TYPE_SUGGESTION,
Objects.requireNonNull(suggestion), null);
}
/** Returns a event of type {@link #EVENT_TYPE_UNCERTAIN}. */
public static TimeZoneProviderEvent createUncertainEvent() {
return UNCERTAIN_EVENT;
}
/** Returns a event of type {@link #EVENT_TYPE_PERMANENT_FAILURE}. */
public static TimeZoneProviderEvent createPermanentFailureEvent(@NonNull String cause) {
return new TimeZoneProviderEvent(EVENT_TYPE_PERMANENT_FAILURE, null,
Objects.requireNonNull(cause));
}
/**
* Returns the event type.
*/
public @EventType int getType() {
return mType;
}
/**
* Returns the suggestion. Populated when {@link #getType()} is {@link #EVENT_TYPE_SUGGESTION}.
*/
@Nullable
public TimeZoneProviderSuggestion getSuggestion() {
return mSuggestion;
}
/**
* Returns the failure cauese. Populated when {@link #getType()} is {@link
* #EVENT_TYPE_PERMANENT_FAILURE}.
*/
@Nullable
public String getFailureCause() {
return mFailureCause;
}
@Override
public String toString() {
return "TimeZoneProviderEvent{"
+ "mType=" + mType
+ ", mSuggestion=" + mSuggestion
+ ", mFailureCause=" + mFailureCause
+ '}';
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TimeZoneProviderEvent that = (TimeZoneProviderEvent) o;
return mType == that.mType
&& Objects.equals(mSuggestion, that.mSuggestion)
&& Objects.equals(mFailureCause, that.mFailureCause);
}
@Override
public int hashCode() {
return Objects.hash(mType, mSuggestion, mFailureCause);
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.location.timezone;
import android.annotation.NonNull;
import android.annotation.Nullable;
import java.time.Duration;
import java.util.Objects;
/**
* This class encapsulates a request to a provider.
*/
final class TimeZoneProviderRequest {
@NonNull
private static final TimeZoneProviderRequest STOP_UPDATES =
new TimeZoneProviderRequest(
false /* sendUpdates */,
null /* initializationTimeout */);
private final boolean mSendUpdates;
@Nullable
private final Duration mInitializationTimeout;
private TimeZoneProviderRequest(
boolean sendUpdates, @Nullable Duration initializationTimeout) {
mSendUpdates = sendUpdates;
mInitializationTimeout = initializationTimeout;
}
/** Creates a request to start updates with the specified timeout. */
public static TimeZoneProviderRequest createStartUpdatesRequest(
@NonNull Duration initializationTimeout) {
return new TimeZoneProviderRequest(true, Objects.requireNonNull(initializationTimeout));
}
/** Creates a request to stop updates. */
public static TimeZoneProviderRequest createStopUpdatesRequest() {
return STOP_UPDATES;
}
/**
* Returns {@code true} if the provider should send updates related to the device's current
* time zone, {@code false} otherwise.
*/
public boolean sendUpdates() {
return mSendUpdates;
}
// TODO(b/152744911) - once there are a couple of implementations, decide whether this needs to
// be passed to the TimeZoneProviderService and remove if it is not useful.
/**
* Returns the maximum time that the provider is allowed to initialize before it is expected to
* send an event of any sort. Only valid when {@link #sendUpdates()} is {@code true}. Failure to
* send an event in this time (with some fuzz) may be interpreted as if the provider is
* uncertain of the time zone, and/or it could lead to the provider being stopped.
*/
@Nullable
public Duration getInitializationTimeout() {
return mInitializationTimeout;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
TimeZoneProviderRequest
that = (TimeZoneProviderRequest) o;
return mSendUpdates == that.mSendUpdates
&& mInitializationTimeout == that.mInitializationTimeout;
}
@Override
public int hashCode() {
return Objects.hash(mSendUpdates, mInitializationTimeout);
}
@Override
public String toString() {
return "TimeZoneProviderRequest{"
+ "mSendUpdates=" + mSendUpdates
+ ", mInitializationTimeout=" + mInitializationTimeout
+ "}";
}
}

View File

@@ -15,17 +15,16 @@
*/
package com.android.server.location.timezone;
import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE;
import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
import static com.android.internal.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_CERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_INITIALIZING;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_UNCERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_DISABLED;
import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
import static com.android.server.location.timezone.TestSupport.USER2_CONFIG_GEO_DETECTION_ENABLED;
import static com.android.server.location.timezone.TimeZoneProviderEvent.createPermanentFailureEvent;
import static com.android.server.location.timezone.TimeZoneProviderEvent.createUncertainEvent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -37,9 +36,9 @@ import static java.util.Arrays.asList;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.platform.test.annotations.Presubmit;
import android.service.timezone.TimeZoneProviderSuggestion;
import android.util.IndentingPrintWriter;
import com.android.internal.location.timezone.LocationTimeZoneEvent;
import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
@@ -61,14 +60,14 @@ public class ControllerImplTest {
private static final long ARBITRARY_TIME_MILLIS = 12345L;
private static final LocationTimeZoneEvent USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1 =
createLocationTimeZoneEvent(EVENT_TYPE_SUCCESS, asList("Europe/London"));
private static final LocationTimeZoneEvent USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2 =
createLocationTimeZoneEvent(EVENT_TYPE_SUCCESS, asList("Europe/Paris"));
private static final LocationTimeZoneEvent USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT =
createLocationTimeZoneEvent(EVENT_TYPE_UNCERTAIN, null);
private static final LocationTimeZoneEvent USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT =
createLocationTimeZoneEvent(EVENT_TYPE_PERMANENT_FAILURE, null);
private static final TimeZoneProviderEvent USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1 =
createSuggestionEvent(asList("Europe/London"));
private static final TimeZoneProviderEvent USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2 =
createSuggestionEvent(asList("Europe/Paris"));
private static final TimeZoneProviderEvent USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT =
createUncertainEvent();
private static final TimeZoneProviderEvent USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT =
createPermanentFailureEvent("Test");
private TestThreadingDomain mTestThreadingDomain;
private TestCallback mTestCallback;
@@ -89,7 +88,7 @@ public class ControllerImplTest {
}
@Test
public void initialState_enabled() {
public void initialState_started() {
ControllerImpl controllerImpl = new ControllerImpl(mTestThreadingDomain,
mTestPrimaryLocationTimeZoneProvider, mTestSecondaryLocationTimeZoneProvider);
TestEnvironment testEnvironment = new TestEnvironment(
@@ -98,16 +97,16 @@ public class ControllerImplTest {
.plus(testEnvironment.getProviderInitializationTimeoutFuzz());
// Initialize. After initialization the providers must be initialized and one should be
// enabled.
// started.
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertInitialized();
mTestSecondaryLocationTimeZoneProvider.assertInitialized();
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestPrimaryLocationTimeZoneProvider.assertInitializationTimeoutSet(expectedInitTimeout);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
}
@@ -120,14 +119,14 @@ public class ControllerImplTest {
mTestThreadingDomain, controllerImpl, USER1_CONFIG_GEO_DETECTION_DISABLED);
// Initialize. After initialization the providers must be initialized but neither should be
// enabled.
// started.
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertInitialized();
mTestSecondaryLocationTimeZoneProvider.assertInitialized();
mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
}
@@ -143,8 +142,8 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
@@ -152,11 +151,11 @@ public class ControllerImplTest {
mTestThreadingDomain.executeNext();
// The primary should have reported uncertainty, which should trigger the controller to
// start the uncertainty timeout and enable the secondary.
// start the uncertainty timeout and start the secondary.
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
@@ -167,9 +166,9 @@ public class ControllerImplTest {
// Now both initialization timeouts should have triggered. The uncertainty timeout should
// still not be triggered.
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
@@ -178,9 +177,9 @@ public class ControllerImplTest {
mTestThreadingDomain.executeNext();
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertUncertainSuggestionMadeAndCommit();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
}
@@ -196,21 +195,21 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate a location event being received from the primary provider. This should cause a
// suggestion to be made.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
}
@@ -225,8 +224,8 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
@@ -234,22 +233,22 @@ public class ControllerImplTest {
mTestThreadingDomain.executeNext();
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
// Simulate a location event being received from the primary provider. This should cause a
// suggestion to be made and the secondary to be shut down.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
}
@@ -264,8 +263,8 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
@@ -273,23 +272,23 @@ public class ControllerImplTest {
mTestThreadingDomain.executeNext();
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
// Simulate a location event being received from the secondary provider. This should cause a
// suggestion to be made.
mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
}
@@ -304,42 +303,42 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate a location event being received from the primary provider. This should cause a
// suggestion to be made.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// A second, identical event should not cause another suggestion.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// And a third, different event should cause another suggestion.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getSuggestion().getTimeZoneIds());
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
}
@@ -354,8 +353,8 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
@@ -363,46 +362,46 @@ public class ControllerImplTest {
mTestThreadingDomain.executeNext();
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
// Simulate a location event being received from the secondary provider. This should cause a
// suggestion to be made.
mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// A second, identical event should not cause another suggestion.
mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// And a third, different event should cause another suggestion.
mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getSuggestion().getTimeZoneIds());
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
}
@@ -417,60 +416,60 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate a location event being received from the primary provider. This should cause a
// suggestion to be made and ensure the primary is considered initialized.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate an uncertain event being received from the primary provider. This should not
// cause a suggestion to be made straight away, but the uncertainty timeout should be
// started and the secondary should be enabled.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
// started and the secondary should be started.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
// Simulate a location event being received from the secondary provider. This should cause a
// suggestion to be made, cancel the uncertainty timeout and ensure the secondary is
// considered initialized.
mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getSuggestion().getTimeZoneIds());
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate an uncertain event being received from the secondary provider. This should not
// cause a suggestion to be made straight away, but the uncertainty timeout should be
// started. Both providers are now enabled, with no initialization timeout set.
mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
// started. Both providers are now started, with no initialization timeout set.
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
@@ -479,9 +478,9 @@ public class ControllerImplTest {
mTestThreadingDomain.executeNext();
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertUncertainSuggestionMadeAndCommit();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
}
@@ -497,46 +496,46 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate a location event being received from the primary provider. This should cause a
// suggestion to be made.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Uncertainty should not cause a suggestion to be made straight away, but the uncertainty
// timeout should be started and the secondary should be enabled.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
// timeout should be started and the secondary should be started.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
// And a success event from the primary provider should cause the controller to make another
// suggestion, the uncertainty timeout should be cancelled and the secondary should be
// disabled again.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
// stopped again.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getSuggestion().getTimeZoneIds());
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
}
@@ -550,8 +549,8 @@ public class ControllerImplTest {
// Initialize and check initial state.
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
@@ -559,16 +558,16 @@ public class ControllerImplTest {
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is disabled.
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
}
@@ -583,8 +582,8 @@ public class ControllerImplTest {
// Initialize and check initial state.
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
@@ -592,20 +591,20 @@ public class ControllerImplTest {
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate a success event being received from the primary provider.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is disabled.
@@ -614,8 +613,8 @@ public class ControllerImplTest {
// of the time zone.
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertUncertainSuggestionMadeAndCommit();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
}
@@ -631,35 +630,35 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate the primary provider suggesting a time zone.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1);
// Receiving a "success" provider event should cause a suggestion to be made synchronously,
// and also clear the scheduled uncertainty suggestion.
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getSuggestion().getTimeZoneIds());
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate the user change (but geo detection still enabled).
testEnvironment.simulateConfigChange(USER2_CONFIG_GEO_DETECTION_ENABLED);
// We expect the provider to end up in PROVIDER_STATE_ENABLED, but it should have been
// disabled when the user changed.
// We expect the provider to end up in PROVIDER_STATE_STARTED_INITIALIZING, but it should
// have been stopped when the user changed.
int[] expectedStateTransitions =
{ PROVIDER_STATE_DISABLED, PROVIDER_STATE_ENABLED_INITIALIZING };
{ PROVIDER_STATE_STOPPED, PROVIDER_STATE_STARTED_INITIALIZING };
mTestPrimaryLocationTimeZoneProvider.assertStateChangesAndCommit(expectedStateTransitions);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfig(
PROVIDER_STATE_ENABLED_INITIALIZING, USER2_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER2_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
}
@@ -674,51 +673,51 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate a failure location event being received from the primary provider. This should
// cause the secondary to be enabled.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
// cause the secondary to be started.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate uncertainty from the secondary.
mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
// And a success event from the secondary provider should cause the controller to make
// another suggestion, the uncertainty timeout should be cancelled.
mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getSuggestion().getTimeZoneIds());
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate uncertainty from the secondary.
mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
}
@@ -734,19 +733,19 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate a failure location event being received from the primary provider. This should
// cause the secondary to be enabled.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
// cause the secondary to be started.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
@@ -754,7 +753,7 @@ public class ControllerImplTest {
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
@@ -763,7 +762,7 @@ public class ControllerImplTest {
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
}
@@ -779,52 +778,52 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate an uncertain event from the primary. This will enable the secondary, which will
// Simulate an uncertain event from the primary. This will start the secondary, which will
// give this test the opportunity to simulate its failure. Then it will be possible to
// demonstrate controller behavior with only the primary working.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
// Simulate failure event from the secondary. This should just affect the secondary's state.
mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
// And a success event from the primary provider should cause the controller to make
// a suggestion, the uncertainty timeout should be cancelled.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_CERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getTimeZoneIds());
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2.getSuggestion().getTimeZoneIds());
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate uncertainty from the primary. The secondary cannot be enabled.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
// Simulate uncertainty from the primary. The secondary cannot be started.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
@@ -841,30 +840,30 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate an uncertain event from the primary. This will enable the secondary, which will
// Simulate an uncertain event from the primary. This will start the secondary, which will
// give this test the opportunity to simulate its failure. Then it will be possible to
// demonstrate controller behavior with only the primary working.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
// Simulate failure event from the secondary. This should just affect the secondary's state.
mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_UNCERTAIN, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertUncertaintyTimeoutSet(testEnvironment, controllerImpl);
@@ -872,17 +871,17 @@ public class ControllerImplTest {
// Now signal a config change so that geo detection is disabled.
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_DISABLED);
mTestPrimaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
mTestPrimaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Now signal a config change so that geo detection is enabled. Only the primary can be
// enabled.
// started.
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
@@ -899,23 +898,23 @@ public class ControllerImplTest {
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestPrimaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsDisabledAndCommit();
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestSecondaryLocationTimeZoneProvider.assertIsStoppedAndCommit();
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate a failure event from the primary. This will enable the secondary.
mTestPrimaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
// Simulate a failure event from the primary. This will start the secondary.
mTestPrimaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
mTestSecondaryLocationTimeZoneProvider.assertStateEnumAndConfigAndCommit(
PROVIDER_STATE_ENABLED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
PROVIDER_STATE_STARTED_INITIALIZING, USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
assertFalse(controllerImpl.isUncertaintyTimeoutSet());
// Simulate failure event from the secondary.
mTestSecondaryLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
mTestSecondaryLocationTimeZoneProvider.simulateTimeZoneProviderEvent(
USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT);
mTestPrimaryLocationTimeZoneProvider.assertIsPermFailedAndCommit();
@@ -932,18 +931,14 @@ public class ControllerImplTest {
controller.getUncertaintyTimeoutDelayMillis());
}
private static LocationTimeZoneEvent createLocationTimeZoneEvent(
int eventType, @Nullable List<String> timeZoneIds) {
LocationTimeZoneEvent.Builder builder = new LocationTimeZoneEvent.Builder()
.setElapsedRealtimeMillis(ARBITRARY_TIME_MILLIS)
.setEventType(eventType);
if (timeZoneIds != null) {
builder.setTimeZoneIds(timeZoneIds);
}
return builder.build();
private static TimeZoneProviderEvent createSuggestionEvent(@NonNull List<String> timeZoneIds) {
return TimeZoneProviderEvent.createSuggestionEvent(
new TimeZoneProviderSuggestion.Builder()
.setElapsedRealtimeMillis(ARBITRARY_TIME_MILLIS)
.setTimeZoneIds(timeZoneIds)
.build());
}
private static class TestEnvironment extends LocationTimeZoneProviderController.Environment {
// These timeouts are set deliberately so that:
@@ -1048,12 +1043,12 @@ public class ControllerImplTest {
}
@Override
void onEnable(Duration initializationTimeout) {
void onStartUpdates(Duration initializationTimeout) {
// Nothing needed for tests.
}
@Override
void onDisable() {
void onStopUpdates() {
// Nothing needed for tests.
}
@@ -1078,9 +1073,9 @@ public class ControllerImplTest {
mTestProviderState.commitLatest();
}
void assertIsDisabledAndCommit() {
// A disabled provider doesn't hold config.
assertStateEnumAndConfig(PROVIDER_STATE_DISABLED, null /* config */);
void assertIsStoppedAndCommit() {
// A stopped provider doesn't hold config.
assertStateEnumAndConfig(PROVIDER_STATE_STOPPED, null /* config */);
mTestProviderState.commitLatest();
}
@@ -1107,7 +1102,7 @@ public class ControllerImplTest {
// If and only if the controller is initializing, the initialization timeout must be
// set.
assertEquals(expectedStateEnum == PROVIDER_STATE_ENABLED_INITIALIZING,
assertEquals(expectedStateEnum == PROVIDER_STATE_STARTED_INITIALIZING,
isInitializationTimeoutSet());
assertConfig(expectedConfig);
@@ -1123,8 +1118,8 @@ public class ControllerImplTest {
assertEquals(expectedTimeout, getInitializationTimeoutDelay());
}
void simulateLocationTimeZoneEvent(@NonNull LocationTimeZoneEvent event) {
handleLocationTimeZoneEvent(event);
void simulateTimeZoneProviderEvent(@NonNull TimeZoneProviderEvent event) {
handleTimeZoneProviderEvent(event);
}
/**

View File

@@ -15,9 +15,9 @@
*/
package com.android.server.location.timezone;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED_INITIALIZING;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
import static org.junit.Assert.assertEquals;
@@ -61,14 +61,14 @@ public class NullLocationTimeZoneProviderTest {
provider.initialize(providerState -> mTestController.onProviderStateChange(providerState));
ProviderState currentState = provider.getCurrentState();
assertEquals(PROVIDER_STATE_DISABLED, currentState.stateEnum);
assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum);
assertNull(currentState.currentUserConfiguration);
assertSame(provider, currentState.provider);
mTestThreadingDomain.assertQueueEmpty();
}
@Test
public void enableSchedulesPermFailure() {
public void startSchedulesPermFailure() {
String providerName = "primary";
NullLocationTimeZoneProvider provider =
new NullLocationTimeZoneProvider(mTestThreadingDomain, providerName);
@@ -77,13 +77,14 @@ public class NullLocationTimeZoneProviderTest {
ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
Duration arbitraryInitializationTimeout = Duration.ofMinutes(5);
Duration arbitraryInitializationTimeoutFuzz = Duration.ofMinutes(2);
provider.enable(config, arbitraryInitializationTimeout, arbitraryInitializationTimeoutFuzz);
provider.startUpdates(config, arbitraryInitializationTimeout,
arbitraryInitializationTimeoutFuzz);
// The NullProvider should enter the enabled state, but have schedule an immediate runnable
// to switch to perm failure.
ProviderState currentState = provider.getCurrentState();
assertSame(provider, currentState.provider);
assertEquals(PROVIDER_STATE_ENABLED_INITIALIZING, currentState.stateEnum);
assertEquals(PROVIDER_STATE_STARTED_INITIALIZING, currentState.stateEnum);
assertEquals(config, currentState.currentUserConfiguration);
mTestThreadingDomain.assertNextQueueItemIsImmediate();
// Entering enabled() does not trigger an onProviderStateChanged() as it is requested by the