Loading services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java +1 −1 Original line number Diff line number Diff line Loading @@ -47,7 +47,7 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider { @NonNull String providerName, @NonNull LocationTimeZoneProviderProxy proxy) { super(providerMetricsLogger, threadingDomain, providerName, new ZoneInfoDbTimeZoneIdValidator()); new ZoneInfoDbTimeZoneProviderEventPreProcessor()); mProxy = Objects.requireNonNull(proxy); } Loading services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java +6 −48 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESU import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY; import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog; import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.infoLog; import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog; import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; Loading Loading @@ -85,18 +84,6 @@ abstract class LocationTimeZoneProvider implements Dumpable { void onProviderStateChange(@NonNull ProviderState providerState); } /** * Used by {@link LocationTimeZoneProvider} to check if time zone IDs are understood * by the platform. */ interface TimeZoneIdValidator { /** * Returns whether {@code timeZoneId} is supported by the platform or not. */ boolean isValid(@NonNull String timeZoneId); } /** * Listener interface used to log provider events for metrics. */ Loading Loading @@ -386,19 +373,20 @@ abstract class LocationTimeZoneProvider implements Dumpable { // Non-null and effectively final after initialize() is called. ProviderListener mProviderListener; @NonNull private TimeZoneIdValidator mTimeZoneIdValidator; @NonNull private final TimeZoneProviderEventPreProcessor mTimeZoneProviderEventPreProcessor; /** Creates the instance. */ LocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger, @NonNull ThreadingDomain threadingDomain, @NonNull String providerName, @NonNull TimeZoneIdValidator timeZoneIdValidator) { @NonNull TimeZoneProviderEventPreProcessor timeZoneProviderEventPreProcessor) { mThreadingDomain = Objects.requireNonNull(threadingDomain); mProviderMetricsLogger = Objects.requireNonNull(providerMetricsLogger); mInitializationTimeoutQueue = threadingDomain.createSingleRunnableQueue(); mSharedLock = threadingDomain.getLockObject(); mProviderName = Objects.requireNonNull(providerName); mTimeZoneIdValidator = Objects.requireNonNull(timeZoneIdValidator); mTimeZoneProviderEventPreProcessor = Objects.requireNonNull(timeZoneProviderEventPreProcessor); } /** Loading Loading @@ -639,24 +627,8 @@ abstract class LocationTimeZoneProvider implements Dumpable { mThreadingDomain.assertCurrentThread(); Objects.requireNonNull(timeZoneProviderEvent); // If the provider has made a suggestion with unknown time zone IDs it cannot be used to set // the device's time zone. This logic prevents bad time zone IDs entering the time zone // detection logic from third party code. // // An event containing an unknown time zone ID could occur if the provider is using a // different TZDB version than the device. Provider developers are expected to take steps to // avoid version skew problem, e.g. by ensuring atomic updates with the platform time zone // rules, or providing IDs based on the device's TZDB version, so this is not considered a // common case. // // Treating a suggestion containing unknown time zone IDs as "uncertain" in the primary // enables immediate failover to a secondary provider, one that might provide valid IDs for // the same location, which should provide better behavior than just ignoring the event. if (hasInvalidTimeZones(timeZoneProviderEvent)) { infoLog("event=" + timeZoneProviderEvent + " has unsupported time zones. " + "Replacing it with uncertain event."); timeZoneProviderEvent = TimeZoneProviderEvent.createUncertainEvent(); } timeZoneProviderEvent = mTimeZoneProviderEventPreProcessor.preProcess(timeZoneProviderEvent); synchronized (mSharedLock) { debugLog("handleTimeZoneProviderEvent: mProviderName=" + mProviderName Loading Loading @@ -755,20 +727,6 @@ abstract class LocationTimeZoneProvider implements Dumpable { } } private boolean hasInvalidTimeZones(@NonNull TimeZoneProviderEvent event) { if (event.getSuggestion() == null) { return false; } for (String timeZone : event.getSuggestion().getTimeZoneIds()) { if (!mTimeZoneIdValidator.isValid(timeZone)) { return true; } } return false; } @GuardedBy("mSharedLock") private void assertIsStarted() { ProviderState currentState = mCurrentState.get(); Loading services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidator.java→services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEventPreProcessor.java +10 −7 Original line number Diff line number Diff line Loading @@ -18,13 +18,16 @@ package com.android.server.timezonedetector.location; import android.annotation.NonNull; import com.android.i18n.timezone.ZoneInfoDb; /** * Used by {@link LocationTimeZoneProvider} to ensure that all time zone IDs are understood by the * platform. */ public interface TimeZoneProviderEventPreProcessor { class ZoneInfoDbTimeZoneIdValidator implements LocationTimeZoneProvider.TimeZoneIdValidator { /** * May return uncertain event if {@code timeZoneProviderEvent} is ill-formed or drop/rewrite * time zone IDs. */ TimeZoneProviderEvent preProcess(@NonNull TimeZoneProviderEvent timeZoneProviderEvent); @Override public boolean isValid(@NonNull String timeZoneId) { return ZoneInfoDb.getInstance().hasTimeZone(timeZoneId); } } services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java 0 → 100644 +72 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.timezonedetector.location; import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.infoLog; import android.annotation.NonNull; import com.android.i18n.timezone.ZoneInfoDb; /** * {@link TimeZoneProviderEventPreProcessor} implementation which makes validations against * {@link ZoneInfoDb}. */ public class ZoneInfoDbTimeZoneProviderEventPreProcessor implements TimeZoneProviderEventPreProcessor { /** * Returns uncertain event if {@code event} has at least one unsupported time zone ID. */ @Override public TimeZoneProviderEvent preProcess(@NonNull TimeZoneProviderEvent event) { if (event.getSuggestion() == null || event.getSuggestion().getTimeZoneIds().isEmpty()) { return event; } // If the provider has made a suggestion with unknown time zone IDs it cannot be used to set // the device's time zone. This logic prevents bad time zone IDs entering the time zone // detection logic from third party code. // // An event containing an unknown time zone ID could occur if the provider is using a // different TZDB version than the device. Provider developers are expected to take steps to // avoid version skew problem, e.g. by ensuring atomic updates with the platform time zone // rules, or providing IDs based on the device's TZDB version, so this is not considered a // common case. // // Treating a suggestion containing unknown time zone IDs as "uncertain" in the primary // enables immediate failover to a secondary provider, one that might provide valid IDs for // the same location, which should provide better behavior than just ignoring the event. if (hasInvalidZones(event)) { return TimeZoneProviderEvent.createUncertainEvent(); } return event; } private static boolean hasInvalidZones(TimeZoneProviderEvent event) { for (String timeZone : event.getSuggestion().getTimeZoneIds()) { if (!ZoneInfoDb.getInstance().hasTimeZone(timeZone)) { infoLog("event=" + event + " has unsupported zone(" + timeZone + ")"); return true; } } return false; } } services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java +4 −19 Original line number Diff line number Diff line Loading @@ -72,7 +72,6 @@ public class ControllerImplTest { private TestCallback mTestCallback; private TestLocationTimeZoneProvider mTestPrimaryLocationTimeZoneProvider; private TestLocationTimeZoneProvider mTestSecondaryLocationTimeZoneProvider; private FakeTimeZoneIdValidator mTimeZoneAvailabilityChecker; @Before public void setUp() { Loading @@ -84,13 +83,10 @@ public class ControllerImplTest { }; mTestThreadingDomain = new TestThreadingDomain(); mTestCallback = new TestCallback(mTestThreadingDomain); mTimeZoneAvailabilityChecker = new FakeTimeZoneIdValidator(); mTestPrimaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider( stubbedProviderMetricsLogger, mTestThreadingDomain, "primary", mTimeZoneAvailabilityChecker); stubbedProviderMetricsLogger, mTestThreadingDomain, "primary"); mTestSecondaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider( stubbedProviderMetricsLogger, mTestThreadingDomain, "secondary", mTimeZoneAvailabilityChecker); stubbedProviderMetricsLogger, mTestThreadingDomain, "secondary"); } @Test Loading Loading @@ -1185,10 +1181,9 @@ public class ControllerImplTest { * Creates the instance. */ TestLocationTimeZoneProvider(ProviderMetricsLogger providerMetricsLogger, ThreadingDomain threadingDomain, String providerName, TimeZoneIdValidator timeZoneIdValidator) { ThreadingDomain threadingDomain, String providerName) { super(providerMetricsLogger, threadingDomain, providerName, timeZoneIdValidator); new FakeTimeZoneProviderEventPreProcessor()); } public void setFailDuringInitialization(boolean failInitialization) { Loading Loading @@ -1321,14 +1316,4 @@ public class ControllerImplTest { mTestProviderState.commitLatest(); } } private static final class FakeTimeZoneIdValidator implements LocationTimeZoneProvider.TimeZoneIdValidator { @Override public boolean isValid(@NonNull String timeZoneId) { return true; } } } Loading
services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java +1 −1 Original line number Diff line number Diff line Loading @@ -47,7 +47,7 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider { @NonNull String providerName, @NonNull LocationTimeZoneProviderProxy proxy) { super(providerMetricsLogger, threadingDomain, providerName, new ZoneInfoDbTimeZoneIdValidator()); new ZoneInfoDbTimeZoneProviderEventPreProcessor()); mProxy = Objects.requireNonNull(proxy); } Loading
services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java +6 −48 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESU import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY; import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog; import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.infoLog; import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog; import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED; import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED; Loading Loading @@ -85,18 +84,6 @@ abstract class LocationTimeZoneProvider implements Dumpable { void onProviderStateChange(@NonNull ProviderState providerState); } /** * Used by {@link LocationTimeZoneProvider} to check if time zone IDs are understood * by the platform. */ interface TimeZoneIdValidator { /** * Returns whether {@code timeZoneId} is supported by the platform or not. */ boolean isValid(@NonNull String timeZoneId); } /** * Listener interface used to log provider events for metrics. */ Loading Loading @@ -386,19 +373,20 @@ abstract class LocationTimeZoneProvider implements Dumpable { // Non-null and effectively final after initialize() is called. ProviderListener mProviderListener; @NonNull private TimeZoneIdValidator mTimeZoneIdValidator; @NonNull private final TimeZoneProviderEventPreProcessor mTimeZoneProviderEventPreProcessor; /** Creates the instance. */ LocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger, @NonNull ThreadingDomain threadingDomain, @NonNull String providerName, @NonNull TimeZoneIdValidator timeZoneIdValidator) { @NonNull TimeZoneProviderEventPreProcessor timeZoneProviderEventPreProcessor) { mThreadingDomain = Objects.requireNonNull(threadingDomain); mProviderMetricsLogger = Objects.requireNonNull(providerMetricsLogger); mInitializationTimeoutQueue = threadingDomain.createSingleRunnableQueue(); mSharedLock = threadingDomain.getLockObject(); mProviderName = Objects.requireNonNull(providerName); mTimeZoneIdValidator = Objects.requireNonNull(timeZoneIdValidator); mTimeZoneProviderEventPreProcessor = Objects.requireNonNull(timeZoneProviderEventPreProcessor); } /** Loading Loading @@ -639,24 +627,8 @@ abstract class LocationTimeZoneProvider implements Dumpable { mThreadingDomain.assertCurrentThread(); Objects.requireNonNull(timeZoneProviderEvent); // If the provider has made a suggestion with unknown time zone IDs it cannot be used to set // the device's time zone. This logic prevents bad time zone IDs entering the time zone // detection logic from third party code. // // An event containing an unknown time zone ID could occur if the provider is using a // different TZDB version than the device. Provider developers are expected to take steps to // avoid version skew problem, e.g. by ensuring atomic updates with the platform time zone // rules, or providing IDs based on the device's TZDB version, so this is not considered a // common case. // // Treating a suggestion containing unknown time zone IDs as "uncertain" in the primary // enables immediate failover to a secondary provider, one that might provide valid IDs for // the same location, which should provide better behavior than just ignoring the event. if (hasInvalidTimeZones(timeZoneProviderEvent)) { infoLog("event=" + timeZoneProviderEvent + " has unsupported time zones. " + "Replacing it with uncertain event."); timeZoneProviderEvent = TimeZoneProviderEvent.createUncertainEvent(); } timeZoneProviderEvent = mTimeZoneProviderEventPreProcessor.preProcess(timeZoneProviderEvent); synchronized (mSharedLock) { debugLog("handleTimeZoneProviderEvent: mProviderName=" + mProviderName Loading Loading @@ -755,20 +727,6 @@ abstract class LocationTimeZoneProvider implements Dumpable { } } private boolean hasInvalidTimeZones(@NonNull TimeZoneProviderEvent event) { if (event.getSuggestion() == null) { return false; } for (String timeZone : event.getSuggestion().getTimeZoneIds()) { if (!mTimeZoneIdValidator.isValid(timeZone)) { return true; } } return false; } @GuardedBy("mSharedLock") private void assertIsStarted() { ProviderState currentState = mCurrentState.get(); Loading
services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneIdValidator.java→services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEventPreProcessor.java +10 −7 Original line number Diff line number Diff line Loading @@ -18,13 +18,16 @@ package com.android.server.timezonedetector.location; import android.annotation.NonNull; import com.android.i18n.timezone.ZoneInfoDb; /** * Used by {@link LocationTimeZoneProvider} to ensure that all time zone IDs are understood by the * platform. */ public interface TimeZoneProviderEventPreProcessor { class ZoneInfoDbTimeZoneIdValidator implements LocationTimeZoneProvider.TimeZoneIdValidator { /** * May return uncertain event if {@code timeZoneProviderEvent} is ill-formed or drop/rewrite * time zone IDs. */ TimeZoneProviderEvent preProcess(@NonNull TimeZoneProviderEvent timeZoneProviderEvent); @Override public boolean isValid(@NonNull String timeZoneId) { return ZoneInfoDb.getInstance().hasTimeZone(timeZoneId); } }
services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java 0 → 100644 +72 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.timezonedetector.location; import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.infoLog; import android.annotation.NonNull; import com.android.i18n.timezone.ZoneInfoDb; /** * {@link TimeZoneProviderEventPreProcessor} implementation which makes validations against * {@link ZoneInfoDb}. */ public class ZoneInfoDbTimeZoneProviderEventPreProcessor implements TimeZoneProviderEventPreProcessor { /** * Returns uncertain event if {@code event} has at least one unsupported time zone ID. */ @Override public TimeZoneProviderEvent preProcess(@NonNull TimeZoneProviderEvent event) { if (event.getSuggestion() == null || event.getSuggestion().getTimeZoneIds().isEmpty()) { return event; } // If the provider has made a suggestion with unknown time zone IDs it cannot be used to set // the device's time zone. This logic prevents bad time zone IDs entering the time zone // detection logic from third party code. // // An event containing an unknown time zone ID could occur if the provider is using a // different TZDB version than the device. Provider developers are expected to take steps to // avoid version skew problem, e.g. by ensuring atomic updates with the platform time zone // rules, or providing IDs based on the device's TZDB version, so this is not considered a // common case. // // Treating a suggestion containing unknown time zone IDs as "uncertain" in the primary // enables immediate failover to a secondary provider, one that might provide valid IDs for // the same location, which should provide better behavior than just ignoring the event. if (hasInvalidZones(event)) { return TimeZoneProviderEvent.createUncertainEvent(); } return event; } private static boolean hasInvalidZones(TimeZoneProviderEvent event) { for (String timeZone : event.getSuggestion().getTimeZoneIds()) { if (!ZoneInfoDb.getInstance().hasTimeZone(timeZone)) { infoLog("event=" + event + " has unsupported zone(" + timeZone + ")"); return true; } } return false; } }
services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java +4 −19 Original line number Diff line number Diff line Loading @@ -72,7 +72,6 @@ public class ControllerImplTest { private TestCallback mTestCallback; private TestLocationTimeZoneProvider mTestPrimaryLocationTimeZoneProvider; private TestLocationTimeZoneProvider mTestSecondaryLocationTimeZoneProvider; private FakeTimeZoneIdValidator mTimeZoneAvailabilityChecker; @Before public void setUp() { Loading @@ -84,13 +83,10 @@ public class ControllerImplTest { }; mTestThreadingDomain = new TestThreadingDomain(); mTestCallback = new TestCallback(mTestThreadingDomain); mTimeZoneAvailabilityChecker = new FakeTimeZoneIdValidator(); mTestPrimaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider( stubbedProviderMetricsLogger, mTestThreadingDomain, "primary", mTimeZoneAvailabilityChecker); stubbedProviderMetricsLogger, mTestThreadingDomain, "primary"); mTestSecondaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider( stubbedProviderMetricsLogger, mTestThreadingDomain, "secondary", mTimeZoneAvailabilityChecker); stubbedProviderMetricsLogger, mTestThreadingDomain, "secondary"); } @Test Loading Loading @@ -1185,10 +1181,9 @@ public class ControllerImplTest { * Creates the instance. */ TestLocationTimeZoneProvider(ProviderMetricsLogger providerMetricsLogger, ThreadingDomain threadingDomain, String providerName, TimeZoneIdValidator timeZoneIdValidator) { ThreadingDomain threadingDomain, String providerName) { super(providerMetricsLogger, threadingDomain, providerName, timeZoneIdValidator); new FakeTimeZoneProviderEventPreProcessor()); } public void setFailDuringInitialization(boolean failInitialization) { Loading Loading @@ -1321,14 +1316,4 @@ public class ControllerImplTest { mTestProviderState.commitLatest(); } } private static final class FakeTimeZoneIdValidator implements LocationTimeZoneProvider.TimeZoneIdValidator { @Override public boolean isValid(@NonNull String timeZoneId) { return true; } } }