Add DeviceConfig settings for loc. tz detection

Add DeviceConfig settings for location time zone detection.

Because the system_time namespace is intended for time AND time zone
detection, the DeviceConfig wrapper has been put in the timedetector
package.

The rest of the work is wiring the config flags to what they affect.

Bug: 178107773
Test: treehugger
Change-Id: Iabbd18f854a95e405d0bb1dcffd44ee86215e562
This commit is contained in:
Neil Fuller
2021-02-01 15:24:28 +00:00
parent 25dd7643ad
commit 7074e8368a
5 changed files with 195 additions and 19 deletions

View File

@@ -0,0 +1,126 @@
/*
* 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);
}
}

View File

@@ -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,18 +112,29 @@ 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() {
if (mConfigChangeListener == null) {
Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null");
synchronized (this) {
if (mConfigChangeListener == null) {
Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null");
}
mConfigChangeListener.onChange();
}
mConfigChangeListener.onChange();
}
@Override
public void setConfigChangeListener(@NonNull ConfigurationChangeListener listener) {
mConfigChangeListener = Objects.requireNonNull(listener);
synchronized (this) {
mConfigChangeListener = Objects.requireNonNull(listener);
}
}
@Override
@@ -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) {

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}