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

Commit 573c8fdf authored by Almaz Mingaleev's avatar Almaz Mingaleev Committed by Neil Fuller
Browse files

Treat time zone suggestions with unrecognized zones as unopinionated.

Bug: 173787057
Test: atest services/tests/servicestests/src/com/android/server/timezonedetector/*
Test: atest
services/tests/servicestests/src/com/android/server/timezonedetector/location/*

Change-Id: I1f700924b89bf0be30a8d19dfaae07e5654e1921
(cherry picked from commit 87f0d3b6)
parent e5290599
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -47,7 +47,7 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider {
            @NonNull ThreadingDomain threadingDomain,
            @NonNull ThreadingDomain threadingDomain,
            @NonNull String providerName,
            @NonNull String providerName,
            @NonNull LocationTimeZoneProviderProxy proxy) {
            @NonNull LocationTimeZoneProviderProxy proxy) {
        super(threadingDomain, providerName);
        super(threadingDomain, providerName, new ZoneInfoDbTimeZoneIdValidator());
        mProxy = Objects.requireNonNull(proxy);
        mProxy = Objects.requireNonNull(proxy);
    }
    }


+6 −0
Original line number Original line Diff line number Diff line
@@ -520,6 +520,12 @@ public class LocationTimeZoneManagerService extends Binder {
        }
        }
    }
    }


    static void infoLog(String msg) {
        if (Log.isLoggable(TAG, Log.INFO)) {
            Slog.i(TAG, msg);
        }
    }

    static void warnLog(String msg) {
    static void warnLog(String msg) {
        warnLog(msg, null);
        warnLog(msg, null);
    }
    }
+51 −1
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESU
import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
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.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.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_DESTROYED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
@@ -84,6 +85,18 @@ abstract class LocationTimeZoneProvider implements Dumpable {
        void onProviderStateChange(@NonNull ProviderState providerState);
        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);
    }

    /**
    /**
     * Information about the provider's current state.
     * Information about the provider's current state.
     */
     */
@@ -364,13 +377,17 @@ abstract class LocationTimeZoneProvider implements Dumpable {
    // Non-null and effectively final after initialize() is called.
    // Non-null and effectively final after initialize() is called.
    ProviderListener mProviderListener;
    ProviderListener mProviderListener;


    @NonNull private TimeZoneIdValidator mTimeZoneIdValidator;

    /** Creates the instance. */
    /** Creates the instance. */
    LocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
    LocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
            @NonNull String providerName) {
            @NonNull String providerName,
            @NonNull TimeZoneIdValidator timeZoneIdValidator) {
        mThreadingDomain = Objects.requireNonNull(threadingDomain);
        mThreadingDomain = Objects.requireNonNull(threadingDomain);
        mInitializationTimeoutQueue = threadingDomain.createSingleRunnableQueue();
        mInitializationTimeoutQueue = threadingDomain.createSingleRunnableQueue();
        mSharedLock = threadingDomain.getLockObject();
        mSharedLock = threadingDomain.getLockObject();
        mProviderName = Objects.requireNonNull(providerName);
        mProviderName = Objects.requireNonNull(providerName);
        mTimeZoneIdValidator = Objects.requireNonNull(timeZoneIdValidator);
    }
    }


    /**
    /**
@@ -610,6 +627,25 @@ abstract class LocationTimeZoneProvider implements Dumpable {
        mThreadingDomain.assertCurrentThread();
        mThreadingDomain.assertCurrentThread();
        Objects.requireNonNull(timeZoneProviderEvent);
        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();
        }

        synchronized (mSharedLock) {
        synchronized (mSharedLock) {
            debugLog("handleTimeZoneProviderEvent: mProviderName=" + mProviderName
            debugLog("handleTimeZoneProviderEvent: mProviderName=" + mProviderName
                    + ", timeZoneProviderEvent=" + timeZoneProviderEvent);
                    + ", timeZoneProviderEvent=" + timeZoneProviderEvent);
@@ -707,6 +743,20 @@ 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")
    @GuardedBy("mSharedLock")
    private void assertIsStarted() {
    private void assertIsStarted() {
        ProviderState currentState = mCurrentState.get();
        ProviderState currentState = mCurrentState.get();
+30 −0
Original line number Original line 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 android.annotation.NonNull;

import com.android.i18n.timezone.ZoneInfoDb;

class ZoneInfoDbTimeZoneIdValidator implements
        LocationTimeZoneProvider.TimeZoneIdValidator {

    @Override
    public boolean isValid(@NonNull String timeZoneId) {
        return ZoneInfoDb.getInstance().hasTimeZone(timeZoneId);
    }
}
+20 −4
Original line number Original line Diff line number Diff line
@@ -72,6 +72,7 @@ public class ControllerImplTest {
    private TestCallback mTestCallback;
    private TestCallback mTestCallback;
    private TestLocationTimeZoneProvider mTestPrimaryLocationTimeZoneProvider;
    private TestLocationTimeZoneProvider mTestPrimaryLocationTimeZoneProvider;
    private TestLocationTimeZoneProvider mTestSecondaryLocationTimeZoneProvider;
    private TestLocationTimeZoneProvider mTestSecondaryLocationTimeZoneProvider;
    private FakeTimeZoneIdValidator mTimeZoneAvailabilityChecker;


    @Before
    @Before
    public void setUp() {
    public void setUp() {
@@ -80,10 +81,13 @@ public class ControllerImplTest {
        // will never get a chance to execute.
        // will never get a chance to execute.
        mTestThreadingDomain = new TestThreadingDomain();
        mTestThreadingDomain = new TestThreadingDomain();
        mTestCallback = new TestCallback(mTestThreadingDomain);
        mTestCallback = new TestCallback(mTestThreadingDomain);
        mTimeZoneAvailabilityChecker = new FakeTimeZoneIdValidator();
        mTestPrimaryLocationTimeZoneProvider =
        mTestPrimaryLocationTimeZoneProvider =
                new TestLocationTimeZoneProvider(mTestThreadingDomain, "primary");
                new TestLocationTimeZoneProvider(
                        mTestThreadingDomain, "primary", mTimeZoneAvailabilityChecker);
        mTestSecondaryLocationTimeZoneProvider =
        mTestSecondaryLocationTimeZoneProvider =
                new TestLocationTimeZoneProvider(mTestThreadingDomain, "secondary");
                new TestLocationTimeZoneProvider(
                        mTestThreadingDomain, "secondary", mTimeZoneAvailabilityChecker);
    }
    }


    @Test
    @Test
@@ -1177,8 +1181,10 @@ public class ControllerImplTest {
        /**
        /**
         * Creates the instance.
         * Creates the instance.
         */
         */
        TestLocationTimeZoneProvider(ThreadingDomain threadingDomain, String providerName) {
        TestLocationTimeZoneProvider(ThreadingDomain threadingDomain,
            super(threadingDomain, providerName);
                String providerName,
                TimeZoneIdValidator timeZoneIdValidator) {
            super(threadingDomain, providerName, timeZoneIdValidator);
        }
        }


        public void setFailDuringInitialization(boolean failInitialization) {
        public void setFailDuringInitialization(boolean failInitialization) {
@@ -1311,4 +1317,14 @@ public class ControllerImplTest {
            mTestProviderState.commitLatest();
            mTestProviderState.commitLatest();
        }
        }
    }
    }

    private static final class FakeTimeZoneIdValidator
            implements LocationTimeZoneProvider.TimeZoneIdValidator {

        @Override
        public boolean isValid(@NonNull String timeZoneId) {
            return true;
        }

    }
}
}
Loading