Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ddd018e6 authored by Almaz Mingaleev's avatar Almaz Mingaleev
Browse files

Add TimeZoneProviderEventPreProcessor.

Now it does the same as TimeZoneIdValidator, but in upcoming CLs
it will map time zone IDs in suggestion to canonical ones.

Bug: 173787057
Test: atest com.android.server.timezonedetector.location
Change-Id: I61fe69c537ea10cde649454646329ee4d99dd69a
parent d355fee3
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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);
    }

+6 −48
Original line number Diff line number Diff line
@@ -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;
@@ -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.
     */
@@ -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);
    }

    /**
@@ -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
@@ -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();
+10 −7
Original line number Diff line number Diff line
@@ -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);
    }
}
+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;
    }

}
+4 −19
Original line number Diff line number Diff line
@@ -72,7 +72,6 @@ public class ControllerImplTest {
    private TestCallback mTestCallback;
    private TestLocationTimeZoneProvider mTestPrimaryLocationTimeZoneProvider;
    private TestLocationTimeZoneProvider mTestSecondaryLocationTimeZoneProvider;
    private FakeTimeZoneIdValidator mTimeZoneAvailabilityChecker;

    @Before
    public void setUp() {
@@ -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
@@ -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) {
@@ -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