Merge "Add DeviceConfig settings for loc. tz detection" into sc-dev

This commit is contained in:
Neil Fuller
2021-02-04 11:41:24 +00:00
committed by Android (Google) Code Review
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.location.LocationManager;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.provider.Settings; import android.provider.Settings;
import android.util.Slog; import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices; import com.android.server.LocalServices;
import com.android.server.timedetector.DeviceConfig;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.Executor;
/** /**
* The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}. * 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 Handler mHandler;
@NonNull private final ContentResolver mCr; @NonNull private final ContentResolver mCr;
@NonNull private final UserManager mUserManager; @NonNull private final UserManager mUserManager;
@NonNull private final DeviceConfig mDeviceConfig;
@NonNull private final boolean mGeoDetectionSupported; @NonNull private final boolean mGeoDetectionSupported;
@NonNull private final LocationManager mLocationManager; @NonNull private final LocationManager mLocationManager;
// @NonNull after setConfigChangeListener() is called. // @NonNull after setConfigChangeListener() is called.
@GuardedBy("this")
private ConfigurationChangeListener mConfigChangeListener; private ConfigurationChangeListener mConfigChangeListener;
EnvironmentImpl(@NonNull Context context, @NonNull Handler handler, EnvironmentImpl(@NonNull Context context, @NonNull Handler handler,
boolean geoDetectionSupported) { @NonNull DeviceConfig deviceConfig, boolean geoDetectionSupported) {
mContext = Objects.requireNonNull(context); mContext = Objects.requireNonNull(context);
mHandler = Objects.requireNonNull(handler); mHandler = Objects.requireNonNull(handler);
Executor handlerExecutor = new HandlerExecutor(mHandler);
mCr = context.getContentResolver(); mCr = context.getContentResolver();
mUserManager = context.getSystemService(UserManager.class); mUserManager = context.getSystemService(UserManager.class);
mLocationManager = context.getSystemService(LocationManager.class); mLocationManager = context.getSystemService(LocationManager.class);
mDeviceConfig = deviceConfig;
mGeoDetectionSupported = geoDetectionSupported; 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. // Listen for the user changing / the user's location mode changing.
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter();
@@ -103,18 +112,29 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
handleConfigChangeOnHandlerThread(); handleConfigChangeOnHandlerThread();
} }
}, UserHandle.USER_ALL); }, 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() { private void handleConfigChangeOnHandlerThread() {
if (mConfigChangeListener == null) { synchronized (this) {
Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null"); if (mConfigChangeListener == null) {
Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null");
}
mConfigChangeListener.onChange();
} }
mConfigChangeListener.onChange();
} }
@Override @Override
public void setConfigChangeListener(@NonNull ConfigurationChangeListener listener) { public void setConfigChangeListener(@NonNull ConfigurationChangeListener listener) {
mConfigChangeListener = Objects.requireNonNull(listener); synchronized (this) {
mConfigChangeListener = Objects.requireNonNull(listener);
}
} }
@Override @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 // 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 // would prevent detecting "default" later. That might influence what happens on later
// releases that support geo detection on the same hardware. // 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(); final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled();
setGeoDetectionEnabledIfRequired(userId, geoTzDetectionEnabled); setGeoDetectionEnabledIfRequired(userId, geoTzDetectionEnabled);
} }
@@ -213,12 +236,25 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
} }
private boolean isGeoDetectionEnabled(@UserIdInt int userId) { 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, return Settings.Secure.getIntForUser(mCr,
Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED, Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
(geoDetectionEnabledByDefault ? 1 : 0) /* defaultValue */, userId) != 0; (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) { private void setGeoDetectionEnabledIfRequired(@UserIdInt int userId, boolean enabled) {
// See comment in setAutoDetectionEnabledIfRequired. http://b/171953500 // See comment in setAutoDetectionEnabledIfRequired. http://b/171953500
if (isGeoDetectionEnabled(userId) != enabled) { 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.GuardedBy;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.server.timedetector.DeviceConfig;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -203,10 +204,11 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
*/ */
public static TimeZoneDetectorStrategyImpl create( public static TimeZoneDetectorStrategyImpl create(
@NonNull Context context, @NonNull Handler handler, @NonNull Context context, @NonNull Handler handler,
boolean geolocationTimeZoneDetectionSupported) { boolean geoDetectionSupported) {
DeviceConfig deviceConfig = new DeviceConfig();
EnvironmentImpl environment = new EnvironmentImpl( EnvironmentImpl environment = new EnvironmentImpl(
context, handler, geolocationTimeZoneDetectionSupported); context, handler, deviceConfig, geoDetectionSupported);
return new TimeZoneDetectorStrategyImpl(environment); return new TimeZoneDetectorStrategyImpl(environment);
} }

View File

@@ -19,6 +19,7 @@ package com.android.server.timezonedetector.location;
import android.annotation.NonNull; import android.annotation.NonNull;
import com.android.server.LocalServices; import com.android.server.LocalServices;
import com.android.server.timedetector.DeviceConfig;
import com.android.server.timezonedetector.ConfigurationChangeListener; import com.android.server.timezonedetector.ConfigurationChangeListener;
import com.android.server.timezonedetector.ConfigurationInternal; import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.TimeZoneDetectorInternal; import com.android.server.timezonedetector.TimeZoneDetectorInternal;
@@ -32,18 +33,22 @@ import java.util.Objects;
*/ */
class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment { class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment {
private static final Duration PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5); private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
private static final Duration PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = Duration.ofMinutes(1); private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
private static final Duration PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5); Duration.ofMinutes(1);
private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
@NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal; @NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal;
@NonNull private final LocationTimeZoneProviderController mController; @NonNull private final LocationTimeZoneProviderController mController;
@NonNull private final DeviceConfig mDeviceConfig;
@NonNull private final ConfigurationChangeListener mConfigurationChangeListener; @NonNull private final ConfigurationChangeListener mConfigurationChangeListener;
ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain, ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain,
@NonNull DeviceConfig deviceConfig,
@NonNull LocationTimeZoneProviderController controller) { @NonNull LocationTimeZoneProviderController controller) {
super(threadingDomain); super(threadingDomain);
mController = Objects.requireNonNull(controller); mController = Objects.requireNonNull(controller);
mDeviceConfig = Objects.requireNonNull(deviceConfig);
mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class); mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class);
// Listen for configuration changes. // Listen for configuration changes.
@@ -66,18 +71,24 @@ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Envir
@Override @Override
@NonNull @NonNull
Duration getProviderInitializationTimeout() { Duration getProviderInitializationTimeout() {
return PROVIDER_INITIALIZATION_TIMEOUT; return mDeviceConfig.getDurationFromMillis(
DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT);
} }
@Override @Override
@NonNull @NonNull
Duration getProviderInitializationTimeoutFuzz() { 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 @Override
@NonNull @NonNull
Duration getUncertaintyDelay() { 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.internal.util.Preconditions;
import com.android.server.FgThread; import com.android.server.FgThread;
import com.android.server.SystemService; import com.android.server.SystemService;
import com.android.server.timedetector.DeviceConfig;
import com.android.server.timezonedetector.TimeZoneDetectorInternal; import com.android.server.timezonedetector.TimeZoneDetectorInternal;
import com.android.server.timezonedetector.TimeZoneDetectorService; import com.android.server.timezonedetector.TimeZoneDetectorService;
@@ -227,10 +228,10 @@ public class LocationTimeZoneManagerService extends Binder {
LocationTimeZoneProvider secondary = createSecondaryProvider(); LocationTimeZoneProvider secondary = createSecondaryProvider();
mLocationTimeZoneDetectorController = mLocationTimeZoneDetectorController =
new ControllerImpl(mThreadingDomain, primary, secondary); new ControllerImpl(mThreadingDomain, primary, secondary);
ControllerCallbackImpl callback = new ControllerCallbackImpl( DeviceConfig deviceConfig = new DeviceConfig();
mThreadingDomain);
mEnvironment = new ControllerEnvironmentImpl( mEnvironment = new ControllerEnvironmentImpl(
mThreadingDomain, mLocationTimeZoneDetectorController); mThreadingDomain, deviceConfig, mLocationTimeZoneDetectorController);
ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
mLocationTimeZoneDetectorController.initialize(mEnvironment, callback); mLocationTimeZoneDetectorController.initialize(mEnvironment, callback);
} }
} }