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

Commit 07ef127c authored by Neil Fuller's avatar Neil Fuller Committed by Automerger Merge Worker
Browse files

Merge "Add DeviceConfig settings for loc. tz detection" into sc-dev am: 4c7adf63

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13462308

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I6eb1c1e844a205bd1ff00085c465eb4eb4e5e0a3
parents 22f156a9 4c7adf63
Loading
Loading
Loading
Loading
+126 −0
Original line number Diff line number Diff line
/*
 * Copyright 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.timedetector;

import static android.provider.DeviceConfig.NAMESPACE_SYSTEM_TIME;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;

import java.time.Duration;
import java.util.concurrent.Executor;

/**
 * A helper class for reading / monitoring the {@link
 * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} namespace for server-configured flags.
 */
public final class DeviceConfig {

    /**
     * An annotation used to indicate when a {@link
     * android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME} key is required.
     *
     * <p>Note that the com.android.geotz module deployment of the Offline LocationTimeZoneProvider
     * also shares the {@link android.provider.DeviceConfig#NAMESPACE_SYSTEM_TIME}, and uses the
     * prefix "geotz_" on all of its key strings.
     */
    @StringDef(prefix = "KEY_", value = {
            KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED,
            KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT,
            KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
            KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
            KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
    })
    @interface DeviceConfigKey {}

    /**
     * The key to force location time zone detection on for a device. Only intended for use during
     * release testing with droidfooders. The user can still disable the feature by turning off the
     * master location switch, or disabling automatic time zone detection.
     */
    @DeviceConfigKey
    public static final String KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED =
            "force_location_time_zone_detection_enabled";

    /**
     * The key for the default value used to determine whether location time zone detection is
     * enabled when the user hasn't explicitly set it yet.
     */
    @DeviceConfigKey
    public static final String KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT =
            "location_time_zone_detection_enabled_default";

    /**
     * The key for the minimum delay after location time zone detection has been enabled before the
     * location time zone manager can report it is uncertain about the time zone.
     */
    @DeviceConfigKey
    public static final String KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS =
            "location_time_zone_detection_uncertainty_delay_millis";

    /**
     * The key for the timeout passed to a location time zone provider that tells it how long it has
     * to provide an explicit first suggestion without being declared uncertain.
     */
    @DeviceConfigKey
    public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS =
            "ltpz_init_timeout_millis";

    /**
     * The key for the extra time added to {@link
     * #KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS} by the location time zone
     * manager before the location time zone provider will actually be declared uncertain.
     */
    @DeviceConfigKey
    public static final String KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS =
            "ltpz_init_timeout_fuzz_millis";

    /** Creates an instance. */
    public DeviceConfig() {}

    /** Adds a listener for the system_time namespace. */
    public void addListener(
            @NonNull Executor handlerExecutor, @NonNull Runnable listener) {
        android.provider.DeviceConfig.addOnPropertiesChangedListener(
                NAMESPACE_SYSTEM_TIME,
                handlerExecutor,
                properties -> listener.run());
    }

    /**
     * Returns a boolean value from {@link android.provider.DeviceConfig} from the system_time
     * namespace, or {@code defaultValue} if there is no explicit value set.
     */
    public boolean getBoolean(@DeviceConfigKey String key, boolean defaultValue) {
        return android.provider.DeviceConfig.getBoolean(NAMESPACE_SYSTEM_TIME, key, defaultValue);
    }

    /**
     * Returns a positive duration from {@link android.provider.DeviceConfig} from the system_time
     * namespace, or {@code defaultValue} if there is no explicit value set.
     */
    @Nullable
    public Duration getDurationFromMillis(
            @DeviceConfigKey String key, @Nullable Duration defaultValue) {
        long deviceConfigValue =
                android.provider.DeviceConfig.getLong(NAMESPACE_SYSTEM_TIME, key, -1);
        if (deviceConfigValue < 0) {
            return defaultValue;
        }
        return Duration.ofMillis(deviceConfigValue);
    }
}
+44 −8
Original line number Diff line number Diff line
@@ -33,15 +33,19 @@ import android.database.ContentObserver;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import com.android.server.timedetector.DeviceConfig;

import java.util.Objects;
import java.util.concurrent.Executor;

/**
 * The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}.
@@ -55,21 +59,26 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
    @NonNull private final Handler mHandler;
    @NonNull private final ContentResolver mCr;
    @NonNull private final UserManager mUserManager;
    @NonNull private final DeviceConfig mDeviceConfig;
    @NonNull private final boolean mGeoDetectionSupported;
    @NonNull private final LocationManager mLocationManager;

    // @NonNull after setConfigChangeListener() is called.
    @GuardedBy("this")
    private ConfigurationChangeListener mConfigChangeListener;

    EnvironmentImpl(@NonNull Context context, @NonNull Handler handler,
            boolean geoDetectionSupported) {
            @NonNull DeviceConfig deviceConfig, boolean geoDetectionSupported) {
        mContext = Objects.requireNonNull(context);
        mHandler = Objects.requireNonNull(handler);
        Executor handlerExecutor = new HandlerExecutor(mHandler);
        mCr = context.getContentResolver();
        mUserManager = context.getSystemService(UserManager.class);
        mLocationManager = context.getSystemService(LocationManager.class);
        mDeviceConfig = deviceConfig;
        mGeoDetectionSupported = geoDetectionSupported;

        // Wire up the change listener. All invocations are performed on the mHandler thread.
        // Wire up the change listeners. All invocations are performed on the mHandler thread.

        // Listen for the user changing / the user's location mode changing.
        IntentFilter filter = new IntentFilter();
@@ -103,19 +112,30 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
                        handleConfigChangeOnHandlerThread();
                    }
                }, UserHandle.USER_ALL);

        // Add async callbacks for changes to server-side flags: some of the flags affect device /
        // user config. All changes can be treated like a config change. If flags that affect config
        // haven't changed then call will be a no-op.
        mDeviceConfig.addListener(
                handlerExecutor,
                this::handleConfigChangeOnHandlerThread);
    }

    private void handleConfigChangeOnHandlerThread() {
        synchronized (this) {
            if (mConfigChangeListener == null) {
                Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null");
            }
            mConfigChangeListener.onChange();
        }
    }

    @Override
    public void setConfigChangeListener(@NonNull ConfigurationChangeListener listener) {
        synchronized (this) {
            mConfigChangeListener = Objects.requireNonNull(listener);
        }
    }

    @Override
    public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
@@ -174,7 +194,10 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
            // time zone detection: if we wrote it down then we'd set the value explicitly, which
            // would prevent detecting "default" later. That might influence what happens on later
            // releases that support geo detection on the same hardware.
            if (isGeoDetectionSupported()) {
            // Also avoid writing the geo detection enabled setting for devices that are currently
            // force-enabled: otherwise we might overwrite a droidfood user's real setting
            // permanently.
            if (isGeoDetectionSupported() && !isGeoDetectionForceEnabled()) {
                final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled();
                setGeoDetectionEnabledIfRequired(userId, geoTzDetectionEnabled);
            }
@@ -213,12 +236,25 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
    }

    private boolean isGeoDetectionEnabled(@UserIdInt int userId) {
        final boolean geoDetectionEnabledByDefault = false;
        // We may never use this, but it gives us a way to force location-based time zone detection
        // on for testers (where their other settings allow).
        boolean forceEnabled = isGeoDetectionForceEnabled();
        if (forceEnabled) {
            return true;
        }

        final boolean geoDetectionEnabledByDefault = mDeviceConfig.getBoolean(
                DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT, false);
        return Settings.Secure.getIntForUser(mCr,
                Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
                (geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0;
    }

    private boolean isGeoDetectionForceEnabled() {
        return mDeviceConfig.getBoolean(
                DeviceConfig.KEY_FORCE_LOCATION_TIME_ZONE_DETECTION_ENABLED, false);
    }

    private void setGeoDetectionEnabledIfRequired(@UserIdInt int userId, boolean enabled) {
        // See comment in setAutoDetectionEnabledIfRequired. http://b/171953500
        if (isGeoDetectionEnabled(userId) != enabled) {
+4 −2
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.timedetector.DeviceConfig;

import java.util.ArrayList;
import java.util.List;
@@ -203,10 +204,11 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
     */
    public static TimeZoneDetectorStrategyImpl create(
            @NonNull Context context, @NonNull Handler handler,
            boolean geolocationTimeZoneDetectionSupported) {
            boolean geoDetectionSupported) {

        DeviceConfig deviceConfig = new DeviceConfig();
        EnvironmentImpl environment = new EnvironmentImpl(
                context, handler, geolocationTimeZoneDetectionSupported);
                context, handler, deviceConfig, geoDetectionSupported);
        return new TimeZoneDetectorStrategyImpl(environment);
    }

+17 −6
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.timezonedetector.location;
import android.annotation.NonNull;

import com.android.server.LocalServices;
import com.android.server.timedetector.DeviceConfig;
import com.android.server.timezonedetector.ConfigurationChangeListener;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
@@ -32,18 +33,22 @@ import java.util.Objects;
 */
class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment {

    private static final Duration PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
    private static final Duration PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = Duration.ofMinutes(1);
    private static final Duration PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
    private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
    private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
            Duration.ofMinutes(1);
    private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);

    @NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal;
    @NonNull private final LocationTimeZoneProviderController mController;
    @NonNull private final DeviceConfig mDeviceConfig;
    @NonNull private final ConfigurationChangeListener mConfigurationChangeListener;

    ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain,
            @NonNull DeviceConfig deviceConfig,
            @NonNull LocationTimeZoneProviderController controller) {
        super(threadingDomain);
        mController = Objects.requireNonNull(controller);
        mDeviceConfig = Objects.requireNonNull(deviceConfig);
        mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class);

        // Listen for configuration changes.
@@ -66,18 +71,24 @@ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Envir
    @Override
    @NonNull
    Duration getProviderInitializationTimeout() {
        return PROVIDER_INITIALIZATION_TIMEOUT;
        return mDeviceConfig.getDurationFromMillis(
                DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
                DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT);
    }

    @Override
    @NonNull
    Duration getProviderInitializationTimeoutFuzz() {
        return PROVIDER_INITIALIZATION_TIMEOUT_FUZZ;
        return mDeviceConfig.getDurationFromMillis(
                DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
                DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ);
    }

    @Override
    @NonNull
    Duration getUncertaintyDelay() {
        return PROVIDER_UNCERTAINTY_DELAY;
        return mDeviceConfig.getDurationFromMillis(
                DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
                DEFAULT_PROVIDER_UNCERTAINTY_DELAY);
    }
}
+4 −3
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.FgThread;
import com.android.server.SystemService;
import com.android.server.timedetector.DeviceConfig;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
import com.android.server.timezonedetector.TimeZoneDetectorService;

@@ -227,10 +228,10 @@ public class LocationTimeZoneManagerService extends Binder {
                    LocationTimeZoneProvider secondary = createSecondaryProvider();
                    mLocationTimeZoneDetectorController =
                            new ControllerImpl(mThreadingDomain, primary, secondary);
                    ControllerCallbackImpl callback = new ControllerCallbackImpl(
                            mThreadingDomain);
                    DeviceConfig deviceConfig = new DeviceConfig();
                    mEnvironment = new ControllerEnvironmentImpl(
                            mThreadingDomain, mLocationTimeZoneDetectorController);
                            mThreadingDomain, deviceConfig, mLocationTimeZoneDetectorController);
                    ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
                    mLocationTimeZoneDetectorController.initialize(mEnvironment, callback);
                }
            }