Merge "Improve server flags / add more server control" into sc-dev
This commit is contained in:
@@ -37,7 +37,7 @@ public final class LocationTimeZoneManager {
|
||||
/**
|
||||
* The name of the service for shell commands
|
||||
*/
|
||||
public static final String SHELL_COMMAND_SERVICE_NAME = "location_time_zone_manager";
|
||||
public static final String SERVICE_NAME = "location_time_zone_manager";
|
||||
|
||||
/**
|
||||
* A shell command that starts the service (after stop).
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* 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 android.content.Context;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.server.timezonedetector.ConfigurationChangeListener;
|
||||
import com.android.server.timezonedetector.ServiceConfigAccessor;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A helper class for reading / monitoring the {@link DeviceConfig#NAMESPACE_SYSTEM_TIME} namespace
|
||||
* for server-configured flags.
|
||||
*/
|
||||
public final class ServerFlags {
|
||||
|
||||
private static final Optional<Boolean> OPTIONAL_TRUE = Optional.of(true);
|
||||
private static final Optional<Boolean> OPTIONAL_FALSE = Optional.of(false);
|
||||
|
||||
/**
|
||||
* An annotation used to indicate when a {@link DeviceConfig#NAMESPACE_SYSTEM_TIME} key is
|
||||
* required.
|
||||
*
|
||||
* <p>Note that the com.android.geotz module deployment of the Offline LocationTimeZoneProvider
|
||||
* also shares the {@link DeviceConfig#NAMESPACE_SYSTEM_TIME}, and uses the
|
||||
* prefix "geotz_" on all of its key strings.
|
||||
*/
|
||||
@StringDef(prefix = "KEY_", value = {
|
||||
KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
|
||||
KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
|
||||
KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
|
||||
KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
|
||||
KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
|
||||
KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
|
||||
KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
|
||||
KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
|
||||
})
|
||||
@interface DeviceConfigKey {}
|
||||
|
||||
/**
|
||||
* Controls whether the location time zone manager service will started. Only observed if
|
||||
* the device build is configured to support location-based time zone detection. See
|
||||
* {@link ServiceConfigAccessor#isGeoTimeZoneDetectionFeatureSupportedInConfig()} and {@link
|
||||
* ServiceConfigAccessor#isGeoTimeZoneDetectionFeatureSupported()}.
|
||||
*/
|
||||
@DeviceConfigKey
|
||||
public static final String KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED =
|
||||
"location_time_zone_detection_feature_supported";
|
||||
|
||||
/**
|
||||
* The key for the server flag that can override the device config for whether the primary
|
||||
* location time zone provider is enabled or disabled.
|
||||
*/
|
||||
@DeviceConfigKey
|
||||
public static final String KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE =
|
||||
"primary_location_time_zone_provider_enabled_override";
|
||||
|
||||
/**
|
||||
* The key for the server flag that can override the device config for whether the secondary
|
||||
* location time zone provider is enabled or disabled.
|
||||
*/
|
||||
@DeviceConfigKey
|
||||
public static final String KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE =
|
||||
"secondary_location_time_zone_provider_enabled_override";
|
||||
|
||||
/**
|
||||
* 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";
|
||||
|
||||
/**
|
||||
* The key for the server flag that can override location time zone detection being enabled for
|
||||
* a user. Only intended for use during release testing with droidfooders. The user can still
|
||||
* disable the feature by turning off the master location switch, or by disabling automatic time
|
||||
* zone detection.
|
||||
*/
|
||||
@DeviceConfigKey
|
||||
public static final String KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE =
|
||||
"location_time_zone_detection_setting_enabled_override";
|
||||
|
||||
/**
|
||||
* 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_SETTING_ENABLED_DEFAULT =
|
||||
"location_time_zone_detection_setting_enabled_default";
|
||||
|
||||
@GuardedBy("mListeners")
|
||||
private final ArrayMap<ConfigurationChangeListener, Set<String>> mListeners = new ArrayMap<>();
|
||||
|
||||
private static final Object SLOCK = new Object();
|
||||
|
||||
@GuardedBy("SLOCK")
|
||||
@Nullable
|
||||
private static ServerFlags sInstance;
|
||||
|
||||
private ServerFlags(Context context) {
|
||||
DeviceConfig.addOnPropertiesChangedListener(
|
||||
NAMESPACE_SYSTEM_TIME,
|
||||
context.getMainExecutor(),
|
||||
this::handlePropertiesChanged);
|
||||
}
|
||||
|
||||
/** Returns the singleton instance. */
|
||||
public static ServerFlags getInstance(Context context) {
|
||||
synchronized (SLOCK) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new ServerFlags(context);
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePropertiesChanged(@NonNull DeviceConfig.Properties properties) {
|
||||
synchronized (mListeners) {
|
||||
for (Map.Entry<ConfigurationChangeListener, Set<String>> listenerEntry
|
||||
: mListeners.entrySet()) {
|
||||
if (intersects(listenerEntry.getValue(), properties.getKeyset())) {
|
||||
listenerEntry.getKey().onChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean intersects(@NonNull Set<String> one, @NonNull Set<String> two) {
|
||||
for (String toFind : one) {
|
||||
if (two.contains(toFind)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for the system_time namespace that will trigger if any of the specified keys
|
||||
* change. Listener callbacks are delivered on the main looper thread.
|
||||
*
|
||||
* <p>Note: Only for use by long-lived objects like other singletons. There is deliberately no
|
||||
* associated remove method.
|
||||
*/
|
||||
public void addListener(@NonNull ConfigurationChangeListener listener,
|
||||
@NonNull Set<String> keys) {
|
||||
Objects.requireNonNull(listener);
|
||||
Objects.requireNonNull(keys);
|
||||
|
||||
synchronized (mListeners) {
|
||||
mListeners.put(listener, keys);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an optional boolean value from {@link DeviceConfig} from the system_time
|
||||
* namespace, returns {@link Optional#empty()} if there is no explicit value set.
|
||||
*/
|
||||
@NonNull
|
||||
public Optional<Boolean> getOptionalBoolean(@DeviceConfigKey String key) {
|
||||
String value = DeviceConfig.getProperty(NAMESPACE_SYSTEM_TIME, key);
|
||||
return parseOptionalBoolean(value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static Optional<Boolean> parseOptionalBoolean(@Nullable String value) {
|
||||
if (value == null) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
return Boolean.parseBoolean(value) ? OPTIONAL_TRUE : OPTIONAL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean value from {@link 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 DeviceConfig.getBoolean(NAMESPACE_SYSTEM_TIME, key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a positive duration from {@link 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 = DeviceConfig.getLong(NAMESPACE_SYSTEM_TIME, key, -1);
|
||||
if (deviceConfigValue < 0) {
|
||||
return defaultValue;
|
||||
}
|
||||
return Duration.ofMillis(deviceConfigValue);
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,11 @@
|
||||
package com.android.server.timezonedetector;
|
||||
|
||||
/**
|
||||
* A listener used to receive notification that time zone configuration has changed.
|
||||
* A listener used to receive notification that configuration has / may have changed (depending on
|
||||
* the usecase).
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ConfigurationChangeListener {
|
||||
/** Called when the current user or a configuration value has changed. */
|
||||
/** Called when the configuration may have changed. */
|
||||
void onChange();
|
||||
}
|
||||
|
||||
@@ -33,26 +33,27 @@ import com.android.internal.util.Preconditions;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Holds all configuration values that affect time zone behavior and some associated logic, e.g.
|
||||
* {@link #getAutoDetectionEnabledBehavior()}, {@link #getGeoDetectionEnabledBehavior()} and {@link
|
||||
* #createCapabilitiesAndConfig()}.
|
||||
* Holds configuration values that affect user-facing time zone behavior and some associated logic.
|
||||
* Some configuration is global, some is user scoped, but this class deliberately doesn't make a
|
||||
* distinction for simplicity.
|
||||
*/
|
||||
public final class ConfigurationInternal {
|
||||
|
||||
private final @UserIdInt int mUserId;
|
||||
private final boolean mUserConfigAllowed;
|
||||
private final boolean mAutoDetectionSupported;
|
||||
private final boolean mGeoDetectionSupported;
|
||||
private final boolean mAutoDetectionEnabled;
|
||||
private final @UserIdInt int mUserId;
|
||||
private final boolean mUserConfigAllowed;
|
||||
private final boolean mLocationEnabled;
|
||||
private final boolean mGeoDetectionEnabled;
|
||||
|
||||
private ConfigurationInternal(Builder builder) {
|
||||
mUserId = builder.mUserId;
|
||||
mUserConfigAllowed = builder.mUserConfigAllowed;
|
||||
mAutoDetectionSupported = builder.mAutoDetectionSupported;
|
||||
mGeoDetectionSupported = builder.mGeoDetectionSupported;
|
||||
mAutoDetectionEnabled = builder.mAutoDetectionEnabled;
|
||||
|
||||
mUserId = builder.mUserId;
|
||||
mUserConfigAllowed = builder.mUserConfigAllowed;
|
||||
mLocationEnabled = builder.mLocationEnabled;
|
||||
mGeoDetectionEnabled = builder.mGeoDetectionEnabled;
|
||||
// if mGeoDetectionSupported then mAutoDetectionSupported, i.e. mGeoDetectionSupported
|
||||
@@ -60,22 +61,6 @@ public final class ConfigurationInternal {
|
||||
Preconditions.checkState(mAutoDetectionSupported || !mGeoDetectionSupported);
|
||||
}
|
||||
|
||||
/** Returns the ID of the user this configuration is associated with. */
|
||||
public @UserIdInt int getUserId() {
|
||||
return mUserId;
|
||||
}
|
||||
|
||||
/** Returns the handle of the user this configuration is associated with. */
|
||||
@NonNull
|
||||
public UserHandle getUserHandle() {
|
||||
return UserHandle.of(mUserId);
|
||||
}
|
||||
|
||||
/** Returns true if the user allowed to modify time zone configuration. */
|
||||
public boolean isUserConfigAllowed() {
|
||||
return mUserConfigAllowed;
|
||||
}
|
||||
|
||||
/** Returns true if the device supports any form of auto time zone detection. */
|
||||
public boolean isAutoDetectionSupported() {
|
||||
return mAutoDetectionSupported;
|
||||
@@ -98,6 +83,22 @@ public final class ConfigurationInternal {
|
||||
return mAutoDetectionSupported && mAutoDetectionEnabled;
|
||||
}
|
||||
|
||||
/** Returns the ID of the user this configuration is associated with. */
|
||||
public @UserIdInt int getUserId() {
|
||||
return mUserId;
|
||||
}
|
||||
|
||||
/** Returns the handle of the user this configuration is associated with. */
|
||||
@NonNull
|
||||
public UserHandle getUserHandle() {
|
||||
return UserHandle.of(mUserId);
|
||||
}
|
||||
|
||||
/** Returns true if the user allowed to modify time zone configuration. */
|
||||
public boolean isUserConfigAllowed() {
|
||||
return mUserConfigAllowed;
|
||||
}
|
||||
|
||||
/** Returns true if user's location can be used generally. */
|
||||
public boolean isLocationEnabled() {
|
||||
return mLocationEnabled;
|
||||
@@ -283,7 +284,7 @@ public final class ConfigurationInternal {
|
||||
/**
|
||||
* Sets whether any form of automatic time zone detection is supported on this device.
|
||||
*/
|
||||
public Builder setAutoDetectionSupported(boolean supported) {
|
||||
public Builder setAutoDetectionFeatureSupported(boolean supported) {
|
||||
mAutoDetectionSupported = supported;
|
||||
return this;
|
||||
}
|
||||
@@ -291,7 +292,7 @@ public final class ConfigurationInternal {
|
||||
/**
|
||||
* Sets whether geolocation time zone detection is supported on this device.
|
||||
*/
|
||||
public Builder setGeoDetectionSupported(boolean supported) {
|
||||
public Builder setGeoDetectionFeatureSupported(boolean supported) {
|
||||
mGeoDetectionSupported = supported;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -31,9 +31,7 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
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;
|
||||
@@ -42,10 +40,9 @@ 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;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* The real implementation of {@link TimeZoneDetectorStrategyImpl.Environment}.
|
||||
@@ -59,8 +56,7 @@ 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 ServiceConfigAccessor mServiceConfigAccessor;
|
||||
@NonNull private final LocationManager mLocationManager;
|
||||
|
||||
// @NonNull after setConfigChangeListener() is called.
|
||||
@@ -68,17 +64,16 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
|
||||
private ConfigurationChangeListener mConfigChangeListener;
|
||||
|
||||
EnvironmentImpl(@NonNull Context context, @NonNull Handler handler,
|
||||
@NonNull DeviceConfig deviceConfig, boolean geoDetectionSupported) {
|
||||
@NonNull ServiceConfigAccessor serviceConfigAccessor) {
|
||||
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;
|
||||
mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
|
||||
|
||||
// Wire up the change listeners. All invocations are performed on the mHandler thread.
|
||||
// Wire up the config 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();
|
||||
@@ -112,13 +107,6 @@ 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() {
|
||||
@@ -140,10 +128,12 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
|
||||
@Override
|
||||
public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
|
||||
return new ConfigurationInternal.Builder(userId)
|
||||
.setUserConfigAllowed(isUserConfigAllowed(userId))
|
||||
.setAutoDetectionSupported(isAutoDetectionSupported())
|
||||
.setGeoDetectionSupported(isGeoDetectionSupported())
|
||||
.setAutoDetectionFeatureSupported(
|
||||
mServiceConfigAccessor.isAutoDetectionFeatureSupported())
|
||||
.setGeoDetectionFeatureSupported(
|
||||
mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported())
|
||||
.setAutoDetectionEnabled(isAutoDetectionEnabled())
|
||||
.setUserConfigAllowed(isUserConfigAllowed(userId))
|
||||
.setLocationEnabled(isLocationEnabled(userId))
|
||||
.setGeoDetectionEnabled(isGeoDetectionEnabled(userId))
|
||||
.build();
|
||||
@@ -186,18 +176,19 @@ 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 new types of auto detection on the same hardware.
|
||||
if (isAutoDetectionSupported()) {
|
||||
if (mServiceConfigAccessor.isAutoDetectionFeatureSupported()) {
|
||||
final boolean autoDetectionEnabled = configuration.isAutoDetectionEnabled();
|
||||
setAutoDetectionEnabledIfRequired(autoDetectionEnabled);
|
||||
|
||||
// Avoid writing the geo detection enabled setting for devices that do not support geo
|
||||
// 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.
|
||||
// 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()) {
|
||||
// Avoid writing the geo detection enabled setting for devices with settings that
|
||||
// are currently overridden by server flags: otherwise we might overwrite a droidfood
|
||||
// user's real setting permanently.
|
||||
// Also avoid writing the geo detection enabled setting for devices that do not support
|
||||
// geo 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 start to support geo detection on the same hardware.
|
||||
if (!mServiceConfigAccessor.getGeoDetectionSettingEnabledOverride().isPresent()
|
||||
&& mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported()) {
|
||||
final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled();
|
||||
setGeoDetectionEnabledIfRequired(userId, geoTzDetectionEnabled);
|
||||
}
|
||||
@@ -209,14 +200,6 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
|
||||
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle);
|
||||
}
|
||||
|
||||
private boolean isAutoDetectionSupported() {
|
||||
return deviceHasTelephonyNetwork() || isGeoDetectionSupported();
|
||||
}
|
||||
|
||||
private boolean isGeoDetectionSupported() {
|
||||
return mGeoDetectionSupported;
|
||||
}
|
||||
|
||||
private boolean isAutoDetectionEnabled() {
|
||||
return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0;
|
||||
}
|
||||
@@ -237,24 +220,20 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
|
||||
|
||||
private boolean isGeoDetectionEnabled(@UserIdInt int userId) {
|
||||
// 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;
|
||||
// on/off for testers (but only where their other settings would allow them to turn it on
|
||||
// for themselves).
|
||||
Optional<Boolean> override = mServiceConfigAccessor.getGeoDetectionSettingEnabledOverride();
|
||||
if (override.isPresent()) {
|
||||
return override.get();
|
||||
}
|
||||
|
||||
final boolean geoDetectionEnabledByDefault = mDeviceConfig.getBoolean(
|
||||
DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_ENABLED_DEFAULT, false);
|
||||
final boolean geoDetectionEnabledByDefault =
|
||||
mServiceConfigAccessor.isGeoDetectionEnabledForUsersByDefault();
|
||||
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) {
|
||||
@@ -262,10 +241,4 @@ public final class EnvironmentImpl implements TimeZoneDetectorStrategyImpl.Envir
|
||||
enabled ? 1 : 0, userId);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean deviceHasTelephonyNetwork() {
|
||||
// TODO b/150583524 Avoid the use of a deprecated API.
|
||||
return mContext.getSystemService(ConnectivityManager.class)
|
||||
.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* 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.timezonedetector;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.server.timedetector.ServerFlags;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A singleton that provides access to service configuration for time zone detection. This hides how
|
||||
* configuration is split between static, compile-time config and dynamic, server-pushed flags. It
|
||||
* provides a rudimentary mechanism to signal when values have changed.
|
||||
*/
|
||||
public final class ServiceConfigAccessor {
|
||||
|
||||
private static final Set<String> SERVER_FLAGS_KEYS_TO_WATCH = Collections.unmodifiableSet(
|
||||
new ArraySet<>(new String[] {
|
||||
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
|
||||
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
|
||||
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
|
||||
ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
|
||||
ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE,
|
||||
ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
|
||||
ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
|
||||
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS
|
||||
}));
|
||||
|
||||
// TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented
|
||||
private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(1);
|
||||
// TODO(b/179488561): Put this back to 1 minute when primary provider is fully implemented
|
||||
private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
|
||||
Duration.ofSeconds(20);
|
||||
private static final Duration DEFAULT_PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
|
||||
|
||||
private static final Object SLOCK = new Object();
|
||||
|
||||
/** The singleton instance. Initialized once in {@link #getInstance(Context)}. */
|
||||
@GuardedBy("SLOCK")
|
||||
@Nullable
|
||||
private static ServiceConfigAccessor sInstance;
|
||||
|
||||
@NonNull private final Context mContext;
|
||||
|
||||
/**
|
||||
* An ultimate "feature switch" for location-based time zone detection. If this is
|
||||
* {@code false}, the device cannot support the feature without a config change or a reboot:
|
||||
* This affects what services are started on boot to minimize expense when the feature is not
|
||||
* wanted.
|
||||
*/
|
||||
private final boolean mGeoDetectionFeatureSupportedInConfig;
|
||||
|
||||
@NonNull private final ServerFlags mServerFlags;
|
||||
|
||||
private ServiceConfigAccessor(@NonNull Context context) {
|
||||
mContext = Objects.requireNonNull(context);
|
||||
|
||||
// The config value is expected to be the main feature flag. Platform developers can also
|
||||
// force enable the feature using a persistent system property. Because system properties
|
||||
// can change, this value is cached and only changes on reboot.
|
||||
mGeoDetectionFeatureSupportedInConfig = context.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection)
|
||||
|| SystemProperties.getBoolean(
|
||||
"persist.sys.location_time_zone_detection_feature_supported", false);
|
||||
|
||||
mServerFlags = ServerFlags.getInstance(mContext);
|
||||
}
|
||||
|
||||
/** Returns the singleton instance. */
|
||||
public static ServiceConfigAccessor getInstance(Context context) {
|
||||
synchronized (SLOCK) {
|
||||
if (sInstance == null) {
|
||||
sInstance = new ServiceConfigAccessor(context);
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener that will be called server flags related to this class change. The callbacks
|
||||
* are delivered on the main looper thread.
|
||||
*
|
||||
* <p>Note: Only for use by long-lived objects. There is deliberately no associated remove
|
||||
* method.
|
||||
*/
|
||||
public void addListener(@NonNull ConfigurationChangeListener listener) {
|
||||
mServerFlags.addListener(listener, SERVER_FLAGS_KEYS_TO_WATCH);
|
||||
}
|
||||
|
||||
/** Returns {@code true} if any form of automatic time zone detection is supported. */
|
||||
public boolean isAutoDetectionFeatureSupported() {
|
||||
return deviceHasTelephonyNetwork() || isGeoTimeZoneDetectionFeatureSupported();
|
||||
}
|
||||
|
||||
private boolean deviceHasTelephonyNetwork() {
|
||||
// TODO b/150583524 Avoid the use of a deprecated API.
|
||||
return mContext.getSystemService(ConnectivityManager.class)
|
||||
.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the location-based time zone detection feature can be supported on
|
||||
* this device at all according to config. When {@code false}, implies that various other
|
||||
* location-based settings will be turned off or rendered meaningless. Typically {@link
|
||||
* #isGeoTimeZoneDetectionFeatureSupported()} should be used instead.
|
||||
*/
|
||||
public boolean isGeoTimeZoneDetectionFeatureSupportedInConfig() {
|
||||
return mGeoDetectionFeatureSupportedInConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the location-based time zone detection feature is supported on the
|
||||
* device. This can be used during feature testing on builds that are capable of location time
|
||||
* zone detection to enable / disable the feature for some users.
|
||||
*/
|
||||
public boolean isGeoTimeZoneDetectionFeatureSupported() {
|
||||
return mGeoDetectionFeatureSupportedInConfig
|
||||
&& isGeoTimeZoneDetectionFeatureSupportedInternal();
|
||||
}
|
||||
|
||||
private boolean isGeoTimeZoneDetectionFeatureSupportedInternal() {
|
||||
final boolean defaultEnabled = true;
|
||||
return mServerFlags.getBoolean(
|
||||
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_FEATURE_SUPPORTED,
|
||||
defaultEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the primary location time zone provider can be used.
|
||||
*/
|
||||
public boolean isPrimaryLocationTimeZoneProviderEnabled() {
|
||||
return getPrimaryLocationTimeZoneProviderEnabledOverride()
|
||||
.orElse(isPrimaryLocationTimeZoneProviderEnabledInConfig());
|
||||
}
|
||||
|
||||
private boolean isPrimaryLocationTimeZoneProviderEnabledInConfig() {
|
||||
int providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider;
|
||||
return getConfigBoolean(providerEnabledConfigId);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Optional<Boolean> getPrimaryLocationTimeZoneProviderEnabledOverride() {
|
||||
return mServerFlags.getOptionalBoolean(
|
||||
ServerFlags.KEY_PRIMARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the secondary location time zone provider can be used.
|
||||
*/
|
||||
public boolean isSecondaryLocationTimeZoneProviderEnabled() {
|
||||
return getSecondaryLocationTimeZoneProviderEnabledOverride()
|
||||
.orElse(isSecondaryLocationTimeZoneProviderEnabledInConfig());
|
||||
}
|
||||
|
||||
private boolean isSecondaryLocationTimeZoneProviderEnabledInConfig() {
|
||||
int providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider;
|
||||
return getConfigBoolean(providerEnabledConfigId);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Optional<Boolean> getSecondaryLocationTimeZoneProviderEnabledOverride() {
|
||||
return mServerFlags.getOptionalBoolean(
|
||||
ServerFlags.KEY_SECONDARY_LOCATION_TIME_ZONE_PROVIDER_ENABLED_OVERRIDE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether location time zone detection is enabled for users when there's no setting
|
||||
* value. Intended for use during feature release testing to "opt-in" users that haven't shown
|
||||
* an explicit preference.
|
||||
*/
|
||||
public boolean isGeoDetectionEnabledForUsersByDefault() {
|
||||
return mServerFlags.getBoolean(
|
||||
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether location time zone detection is force enabled/disabled for users. Intended
|
||||
* for use during feature release testing to force a given state.
|
||||
*/
|
||||
@NonNull
|
||||
public Optional<Boolean> getGeoDetectionSettingEnabledOverride() {
|
||||
return mServerFlags.getOptionalBoolean(
|
||||
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time to send to a location time zone provider that informs it how long it has
|
||||
* to return its first time zone suggestion.
|
||||
*/
|
||||
@NonNull
|
||||
public Duration getLocationTimeZoneProviderInitializationTimeout() {
|
||||
return mServerFlags.getDurationFromMillis(
|
||||
ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
|
||||
DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time added to {@link #getLocationTimeZoneProviderInitializationTimeout()} by the
|
||||
* server before unilaterally declaring the provider is uncertain.
|
||||
*/
|
||||
@NonNull
|
||||
public Duration getLocationTimeZoneProviderInitializationTimeoutFuzz() {
|
||||
return mServerFlags.getDurationFromMillis(
|
||||
ServerFlags.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
|
||||
DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time after uncertainty is detected by providers before the location time zone
|
||||
* manager makes a suggestion to the time zone detector.
|
||||
*/
|
||||
@NonNull
|
||||
public Duration getLocationTimeZoneUncertaintyDelay() {
|
||||
return mServerFlags.getDurationFromMillis(
|
||||
ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
|
||||
DEFAULT_PROVIDER_UNCERTAINTY_DELAY);
|
||||
}
|
||||
|
||||
private boolean getConfigBoolean(int providerEnabledConfigId) {
|
||||
Resources resources = mContext.getResources();
|
||||
return resources.getBoolean(providerEnabledConfigId);
|
||||
}
|
||||
}
|
||||
@@ -27,16 +27,21 @@ import android.annotation.NonNull;
|
||||
*/
|
||||
public interface TimeZoneDetectorInternal extends Dumpable.Container {
|
||||
|
||||
/** Adds a listener that will be invoked when time zone detection configuration is changed. */
|
||||
void addConfigurationListener(ConfigurationChangeListener listener);
|
||||
/** Adds a listener that will be invoked when {@link ConfigurationInternal} may have changed. */
|
||||
void addConfigurationListener(@NonNull ConfigurationChangeListener listener);
|
||||
|
||||
/**
|
||||
* Removes a listener previously added via {@link
|
||||
* #addConfigurationListener(ConfigurationChangeListener)}.
|
||||
*/
|
||||
void removeConfigurationListener(ConfigurationChangeListener listener);
|
||||
void removeConfigurationListener(@NonNull ConfigurationChangeListener listener);
|
||||
|
||||
/** Returns the {@link ConfigurationInternal} for the current user. */
|
||||
/**
|
||||
* Returns a snapshot of the {@link ConfigurationInternal} for the current user. This is only a
|
||||
* snapshot so callers must use {@link #addConfigurationListener(ConfigurationChangeListener)}
|
||||
* to be notified when it changes.
|
||||
*/
|
||||
@NonNull
|
||||
ConfigurationInternal getCurrentUserConfigurationInternal();
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,6 @@ import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ResultReceiver;
|
||||
import android.os.ShellCallback;
|
||||
import android.os.SystemProperties;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.Slog;
|
||||
@@ -61,27 +60,6 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
|
||||
|
||||
static final String TAG = "time_zone_detector";
|
||||
|
||||
/**
|
||||
* A "feature switch" for location-based time zone detection. If this is {@code false}. It is
|
||||
* initialized and never refreshed; it affects what services are started on boot so consistency
|
||||
* is important.
|
||||
*/
|
||||
@Nullable
|
||||
private static Boolean sGeoLocationTimeZoneDetectionSupported;
|
||||
|
||||
/** Returns {@code true} if the location-based time zone detection feature is enabled. */
|
||||
public static boolean isGeoLocationTimeZoneDetectionSupported(Context context) {
|
||||
if (sGeoLocationTimeZoneDetectionSupported == null) {
|
||||
// The config value is expected to be the main switch. Platform developers can also
|
||||
// enable the feature using a persistent system property.
|
||||
sGeoLocationTimeZoneDetectionSupported = context.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection)
|
||||
|| SystemProperties.getBoolean(
|
||||
"persist.sys.location_time_zone_detection_feature_enabled", false);
|
||||
}
|
||||
return sGeoLocationTimeZoneDetectionSupported;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the service lifecycle for {@link TimeZoneDetectorService} and
|
||||
* {@link TimeZoneDetectorInternalImpl}.
|
||||
@@ -98,11 +76,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
|
||||
Context context = getContext();
|
||||
Handler handler = FgThread.getHandler();
|
||||
|
||||
boolean geolocationTimeZoneDetectionSupported =
|
||||
isGeoLocationTimeZoneDetectionSupported(context);
|
||||
ServiceConfigAccessor serviceConfigAccessor =
|
||||
ServiceConfigAccessor.getInstance(context);
|
||||
TimeZoneDetectorStrategy timeZoneDetectorStrategy =
|
||||
TimeZoneDetectorStrategyImpl.create(
|
||||
context, handler, geolocationTimeZoneDetectionSupported);
|
||||
TimeZoneDetectorStrategyImpl.create(context, handler, serviceConfigAccessor);
|
||||
|
||||
// Create and publish the local service for use by internal callers.
|
||||
TimeZoneDetectorInternal internal =
|
||||
@@ -330,7 +307,8 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub
|
||||
boolean isGeoTimeZoneDetectionSupported() {
|
||||
enforceManageTimeZoneDetectorPermission();
|
||||
|
||||
return isGeoLocationTimeZoneDetectionSupported(mContext);
|
||||
return ServiceConfigAccessor.getInstance(mContext)
|
||||
.isGeoTimeZoneDetectionFeatureSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -75,17 +75,21 @@ import android.util.IndentingPrintWriter;
|
||||
public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container {
|
||||
|
||||
/**
|
||||
* Sets a listener that will be triggered whenever time zone detection configuration is
|
||||
* Adds a listener that will be triggered whenever {@link ConfigurationInternal} may have
|
||||
* changed.
|
||||
*/
|
||||
void addConfigChangeListener(@NonNull ConfigurationChangeListener listener);
|
||||
|
||||
/** Returns the user's time zone configuration. */
|
||||
/**
|
||||
* Returns a snapshot of the configuration that controls time zone detector behavior for the
|
||||
* specified user.
|
||||
*/
|
||||
@NonNull
|
||||
ConfigurationInternal getConfigurationInternal(@UserIdInt int userId);
|
||||
|
||||
/**
|
||||
* Returns the configuration that controls time zone detector behavior for the current user.
|
||||
* Returns a snapshot of the configuration that controls time zone detector behavior for the
|
||||
* current user.
|
||||
*/
|
||||
@NonNull
|
||||
ConfigurationInternal getCurrentUserConfigurationInternal();
|
||||
|
||||
@@ -38,7 +38,6 @@ 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;
|
||||
@@ -204,11 +203,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat
|
||||
*/
|
||||
public static TimeZoneDetectorStrategyImpl create(
|
||||
@NonNull Context context, @NonNull Handler handler,
|
||||
boolean geoDetectionSupported) {
|
||||
@NonNull ServiceConfigAccessor serviceConfigAccessor) {
|
||||
|
||||
DeviceConfig deviceConfig = new DeviceConfig();
|
||||
EnvironmentImpl environment = new EnvironmentImpl(
|
||||
context, handler, deviceConfig, geoDetectionSupported);
|
||||
Environment environment = new EnvironmentImpl(context, handler, serviceConfigAccessor);
|
||||
return new TimeZoneDetectorStrategyImpl(environment);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,9 +19,9 @@ 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.ServiceConfigAccessor;
|
||||
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
|
||||
|
||||
import java.time.Duration;
|
||||
@@ -33,28 +33,19 @@ import java.util.Objects;
|
||||
*/
|
||||
class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment {
|
||||
|
||||
// TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented
|
||||
private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(1);
|
||||
// TODO(b/179488561): Put this back to 5 minutes when primary provider is fully implemented
|
||||
private static final Duration DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ =
|
||||
Duration.ofSeconds(20);
|
||||
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 ServiceConfigAccessor mServiceConfigAccessor;
|
||||
@NonNull private final ConfigurationChangeListener mConfigurationChangeListener;
|
||||
|
||||
ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain,
|
||||
@NonNull DeviceConfig deviceConfig,
|
||||
@NonNull ServiceConfigAccessor serviceConfigAccessor,
|
||||
@NonNull LocationTimeZoneProviderController controller) {
|
||||
super(threadingDomain);
|
||||
mController = Objects.requireNonNull(controller);
|
||||
mDeviceConfig = Objects.requireNonNull(deviceConfig);
|
||||
mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
|
||||
mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class);
|
||||
|
||||
// Listen for configuration changes.
|
||||
mConfigurationChangeListener = () -> mThreadingDomain.post(mController::onConfigChanged);
|
||||
mConfigurationChangeListener = () -> mThreadingDomain.post(controller::onConfigChanged);
|
||||
mTimeZoneDetectorInternal.addConfigurationListener(mConfigurationChangeListener);
|
||||
}
|
||||
|
||||
@@ -73,24 +64,18 @@ class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Envir
|
||||
@Override
|
||||
@NonNull
|
||||
Duration getProviderInitializationTimeout() {
|
||||
return mDeviceConfig.getDurationFromMillis(
|
||||
DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_MILLIS,
|
||||
DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT);
|
||||
return mServiceConfigAccessor.getLocationTimeZoneProviderInitializationTimeout();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
Duration getProviderInitializationTimeoutFuzz() {
|
||||
return mDeviceConfig.getDurationFromMillis(
|
||||
DeviceConfig.KEY_LOCATION_TIME_ZONE_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ_MILLIS,
|
||||
DEFAULT_PROVIDER_INITIALIZATION_TIMEOUT_FUZZ);
|
||||
return mServiceConfigAccessor.getLocationTimeZoneProviderInitializationTimeoutFuzz();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
Duration getUncertaintyDelay() {
|
||||
return mDeviceConfig.getDurationFromMillis(
|
||||
DeviceConfig.KEY_LOCATION_TIME_ZONE_DETECTION_UNCERTAINTY_DELAY_MILLIS,
|
||||
DEFAULT_PROVIDER_UNCERTAINTY_DELAY);
|
||||
return mServiceConfigAccessor.getLocationTimeZoneUncertaintyDelay();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,11 @@ import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DI
|
||||
import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE;
|
||||
import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED;
|
||||
import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
|
||||
import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@@ -43,9 +43,8 @@ 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.ServiceConfigAccessor;
|
||||
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
|
||||
import com.android.server.timezonedetector.TimeZoneDetectorService;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
@@ -96,28 +95,31 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
|
||||
private LocationTimeZoneManagerService mService;
|
||||
|
||||
@NonNull
|
||||
private final ServiceConfigAccessor mServerConfigAccessor;
|
||||
|
||||
public Lifecycle(@NonNull Context context) {
|
||||
super(Objects.requireNonNull(context));
|
||||
mServerConfigAccessor = ServiceConfigAccessor.getInstance(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
Context context = getContext();
|
||||
if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) {
|
||||
if (mServerConfigAccessor.isGeoTimeZoneDetectionFeatureSupportedInConfig()) {
|
||||
mService = new LocationTimeZoneManagerService(context);
|
||||
|
||||
// The service currently exposes no LocalService or Binder API, but it extends
|
||||
// Binder and is registered as a binder service so it can receive shell commands.
|
||||
publishBinderService("location_time_zone_manager", mService);
|
||||
publishBinderService(SERVICE_NAME, mService);
|
||||
} else {
|
||||
Slog.i(TAG, getClass() + " is disabled");
|
||||
Slog.d(TAG, "Geo time zone detection feature is disabled in config");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBootPhase(int phase) {
|
||||
Context context = getContext();
|
||||
if (TimeZoneDetectorService.isGeoLocationTimeZoneDetectionSupported(context)) {
|
||||
public void onBootPhase(@BootPhase int phase) {
|
||||
if (mServerConfigAccessor.isGeoTimeZoneDetectionFeatureSupportedInConfig()) {
|
||||
if (phase == PHASE_SYSTEM_SERVICES_READY) {
|
||||
// The location service must be functioning after this boot phase.
|
||||
mService.onSystemReady();
|
||||
@@ -159,6 +161,9 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
/** The shared lock from {@link #mThreadingDomain}. */
|
||||
@NonNull private final Object mSharedLock;
|
||||
|
||||
@NonNull
|
||||
private final ServiceConfigAccessor mServiceConfigAccessor;
|
||||
|
||||
// Lazily initialized. Can be null if the service has been stopped.
|
||||
@GuardedBy("mSharedLock")
|
||||
private ControllerImpl mLocationTimeZoneDetectorController;
|
||||
@@ -180,24 +185,38 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
mHandler = FgThread.getHandler();
|
||||
mThreadingDomain = new HandlerThreadingDomain(mHandler);
|
||||
mSharedLock = mThreadingDomain.getLockObject();
|
||||
mServiceConfigAccessor = ServiceConfigAccessor.getInstance(mContext);
|
||||
}
|
||||
|
||||
// According to the SystemService docs: All lifecycle methods are called from the system
|
||||
// server's main looper thread.
|
||||
void onSystemReady() {
|
||||
// Called on an arbitrary thread during initialization.
|
||||
synchronized (mSharedLock) {
|
||||
// TODO(b/152744911): LocationManagerService watches for packages disappearing. Need to
|
||||
// do anything here?
|
||||
mServiceConfigAccessor.addListener(this::handleServiceConfigurationChangedOnMainThread);
|
||||
}
|
||||
|
||||
// TODO(b/152744911): LocationManagerService watches for foreground app changes. Need to
|
||||
// do anything here?
|
||||
// TODO(b/152744911): LocationManagerService watches screen state. Need to do anything
|
||||
// here?
|
||||
private void handleServiceConfigurationChangedOnMainThread() {
|
||||
// This method is called on the main thread, but service logic takes place on the threading
|
||||
// domain thread, so we post the work there.
|
||||
|
||||
// The way all service-level configuration changes are handled is to just restart this
|
||||
// service - this is simple and effective, and service configuration changes should be rare.
|
||||
mThreadingDomain.post(this::restartIfRequiredOnDomainThread);
|
||||
}
|
||||
|
||||
private void restartIfRequiredOnDomainThread() {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
|
||||
synchronized (mSharedLock) {
|
||||
// Stop and start the service, waiting until completion.
|
||||
stopOnDomainThread();
|
||||
startOnDomainThread();
|
||||
}
|
||||
}
|
||||
|
||||
// According to the SystemService docs: All lifecycle methods are called from the system
|
||||
// server's main looper thread.
|
||||
void onSystemThirdPartyAppsCanStart() {
|
||||
// Called on an arbitrary thread during initialization. We do not want to wait for
|
||||
// completion as it would delay boot.
|
||||
// Do not wait for completion as it would delay boot.
|
||||
final boolean waitForCompletion = false;
|
||||
startInternal(waitForCompletion);
|
||||
}
|
||||
@@ -205,6 +224,9 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
/**
|
||||
* Starts the service during server initialization or during tests after a call to
|
||||
* {@link #stop()}.
|
||||
*
|
||||
* <p>Because this method posts work to the {@code mThreadingDomain} thread and waits for
|
||||
* completion, it cannot be called from the {@code mThreadingDomain} thread.
|
||||
*/
|
||||
void start() {
|
||||
enforceManageTimeZoneDetectorPermission();
|
||||
@@ -214,28 +236,17 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the service during server initialization or during tests after a call to
|
||||
* {@link #stop()}.
|
||||
* Starts the service during server initialization, if the configuration changes or during tests
|
||||
* after a call to {@link #stop()}.
|
||||
*
|
||||
* <p>To avoid tests needing to sleep, when {@code waitForCompletion} is {@code true}, this
|
||||
* method will not return until all the system server components have started.
|
||||
*
|
||||
* <p>Because this method posts work to the {@code mThreadingDomain} thread, it cannot be
|
||||
* called from the {@code mThreadingDomain} thread when {@code waitForCompletion} is true.
|
||||
*/
|
||||
private void startInternal(boolean waitForCompletion) {
|
||||
Runnable runnable = () -> {
|
||||
synchronized (mSharedLock) {
|
||||
if (mLocationTimeZoneDetectorController == null) {
|
||||
LocationTimeZoneProvider primary = createPrimaryProvider();
|
||||
LocationTimeZoneProvider secondary = createSecondaryProvider();
|
||||
mLocationTimeZoneDetectorController =
|
||||
new ControllerImpl(mThreadingDomain, primary, secondary);
|
||||
DeviceConfig deviceConfig = new DeviceConfig();
|
||||
mEnvironment = new ControllerEnvironmentImpl(
|
||||
mThreadingDomain, deviceConfig, mLocationTimeZoneDetectorController);
|
||||
ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
|
||||
mLocationTimeZoneDetectorController.initialize(mEnvironment, callback);
|
||||
}
|
||||
}
|
||||
};
|
||||
Runnable runnable = this::startOnDomainThread;
|
||||
if (waitForCompletion) {
|
||||
mThreadingDomain.postAndWait(runnable, BLOCKING_OP_WAIT_DURATION_MILLIS);
|
||||
} else {
|
||||
@@ -243,11 +254,38 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
}
|
||||
}
|
||||
|
||||
private void startOnDomainThread() {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
|
||||
synchronized (mSharedLock) {
|
||||
if (!mServiceConfigAccessor.isGeoTimeZoneDetectionFeatureSupported()) {
|
||||
debugLog("Not starting " + SERVICE_NAME + ": it is disabled in service config");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mLocationTimeZoneDetectorController == null) {
|
||||
LocationTimeZoneProvider primary = createPrimaryProvider();
|
||||
LocationTimeZoneProvider secondary = createSecondaryProvider();
|
||||
|
||||
ControllerImpl controller =
|
||||
new ControllerImpl(mThreadingDomain, primary, secondary);
|
||||
ControllerEnvironmentImpl environment = new ControllerEnvironmentImpl(
|
||||
mThreadingDomain, mServiceConfigAccessor, controller);
|
||||
ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
|
||||
controller.initialize(environment, callback);
|
||||
|
||||
mEnvironment = environment;
|
||||
mLocationTimeZoneDetectorController = controller;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private LocationTimeZoneProvider createPrimaryProvider() {
|
||||
LocationTimeZoneProviderProxy proxy;
|
||||
if (isProviderInSimulationMode(PRIMARY_PROVIDER_NAME)) {
|
||||
proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
|
||||
} else if (isProviderDisabled(PRIMARY_PROVIDER_NAME)) {
|
||||
} else if (!isProviderEnabled(PRIMARY_PROVIDER_NAME)) {
|
||||
proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
|
||||
} else {
|
||||
proxy = new RealLocationTimeZoneProviderProxy(
|
||||
@@ -262,11 +300,12 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
return new BinderLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private LocationTimeZoneProvider createSecondaryProvider() {
|
||||
LocationTimeZoneProviderProxy proxy;
|
||||
if (isProviderInSimulationMode(SECONDARY_PROVIDER_NAME)) {
|
||||
proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
|
||||
} else if (isProviderDisabled(SECONDARY_PROVIDER_NAME)) {
|
||||
} else if (!isProviderEnabled(SECONDARY_PROVIDER_NAME)) {
|
||||
proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
|
||||
} else {
|
||||
proxy = new RealLocationTimeZoneProviderProxy(
|
||||
@@ -282,33 +321,27 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
}
|
||||
|
||||
/** Used for bug triage and in tests to simulate provider events. */
|
||||
private boolean isProviderInSimulationMode(String providerName) {
|
||||
private boolean isProviderInSimulationMode(@NonNull String providerName) {
|
||||
return isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_SIMULATED);
|
||||
}
|
||||
|
||||
/** Used for bug triage, tests and experiments to remove a provider. */
|
||||
private boolean isProviderDisabled(String providerName) {
|
||||
return !isProviderEnabledInConfig(providerName)
|
||||
|| isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_DISABLED);
|
||||
}
|
||||
/** Used for bug triage, and by tests and experiments to remove a provider. */
|
||||
private boolean isProviderEnabled(@NonNull String providerName) {
|
||||
if (isProviderModeOverrideSet(providerName, PROVIDER_MODE_OVERRIDE_DISABLED)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isProviderEnabledInConfig(String providerName) {
|
||||
int providerEnabledConfigId;
|
||||
switch (providerName) {
|
||||
case PRIMARY_PROVIDER_NAME: {
|
||||
providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider;
|
||||
break;
|
||||
return mServiceConfigAccessor.isPrimaryLocationTimeZoneProviderEnabled();
|
||||
}
|
||||
case SECONDARY_PROVIDER_NAME: {
|
||||
providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider;
|
||||
break;
|
||||
return mServiceConfigAccessor.isSecondaryLocationTimeZoneProviderEnabled();
|
||||
}
|
||||
default: {
|
||||
throw new IllegalArgumentException(providerName);
|
||||
}
|
||||
}
|
||||
Resources resources = mContext.getResources();
|
||||
return resources.getBoolean(providerEnabledConfigId);
|
||||
}
|
||||
|
||||
private boolean isProviderModeOverrideSet(@NonNull String providerName, @NonNull String mode) {
|
||||
@@ -326,22 +359,29 @@ public class LocationTimeZoneManagerService extends Binder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the service for tests. To avoid tests needing to sleep, this method will not return
|
||||
* until all the system server components have stopped.
|
||||
* Stops the service for tests and other rare cases. To avoid tests needing to sleep, this
|
||||
* method will not return until all the system server components have stopped.
|
||||
*
|
||||
* <p>Because this method posts work to the {@code mThreadingDomain} thread and waits it cannot
|
||||
* be called from the {@code mThreadingDomain} thread.
|
||||
*/
|
||||
void stop() {
|
||||
enforceManageTimeZoneDetectorPermission();
|
||||
|
||||
mThreadingDomain.postAndWait(() -> {
|
||||
synchronized (mSharedLock) {
|
||||
if (mLocationTimeZoneDetectorController != null) {
|
||||
mLocationTimeZoneDetectorController.destroy();
|
||||
mLocationTimeZoneDetectorController = null;
|
||||
mEnvironment.destroy();
|
||||
mEnvironment = null;
|
||||
}
|
||||
mThreadingDomain.postAndWait(this::stopOnDomainThread, BLOCKING_OP_WAIT_DURATION_MILLIS);
|
||||
}
|
||||
|
||||
private void stopOnDomainThread() {
|
||||
mThreadingDomain.assertCurrentThread();
|
||||
|
||||
synchronized (mSharedLock) {
|
||||
if (mLocationTimeZoneDetectorController != null) {
|
||||
mLocationTimeZoneDetectorController.destroy();
|
||||
mLocationTimeZoneDetectorController = null;
|
||||
mEnvironment.destroy();
|
||||
mEnvironment = null;
|
||||
}
|
||||
}, BLOCKING_OP_WAIT_DURATION_MILLIS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,6 +21,7 @@ import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DI
|
||||
import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_NONE;
|
||||
import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_SIMULATED;
|
||||
import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
|
||||
import static android.app.time.LocationTimeZoneManager.SERVICE_NAME;
|
||||
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_DUMP_STATE;
|
||||
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_RECORD_PROVIDER_STATES;
|
||||
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND;
|
||||
@@ -102,7 +103,7 @@ class LocationTimeZoneManagerShellCommand extends ShellCommand {
|
||||
@Override
|
||||
public void onHelp() {
|
||||
final PrintWriter pw = getOutPrintWriter();
|
||||
pw.println("Location Time Zone Manager (location_time_zone_manager) commands for tests:");
|
||||
pw.printf("Location Time Zone Manager (%s) commands for tests:\n", SERVICE_NAME);
|
||||
pw.println(" help");
|
||||
pw.println(" Print this help text.");
|
||||
pw.printf(" %s\n", SHELL_COMMAND_START);
|
||||
|
||||
@@ -63,8 +63,6 @@ final class TimeZoneProviderRequest {
|
||||
return mSendUpdates;
|
||||
}
|
||||
|
||||
// TODO(b/152744911) - once there are a couple of implementations, decide whether this needs to
|
||||
// be passed to the TimeZoneProviderService and remove if it is not useful.
|
||||
/**
|
||||
* Returns the maximum time that the provider is allowed to initialize before it is expected to
|
||||
* send an event of any sort. Only valid when {@link #sendUpdates()} is {@code true}. Failure to
|
||||
|
||||
@@ -46,8 +46,8 @@ public class ConfigurationInternalTest {
|
||||
public void test_unrestricted() {
|
||||
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
|
||||
.setUserConfigAllowed(true)
|
||||
.setAutoDetectionSupported(true)
|
||||
.setGeoDetectionSupported(true)
|
||||
.setAutoDetectionFeatureSupported(true)
|
||||
.setGeoDetectionFeatureSupported(true)
|
||||
.setAutoDetectionEnabled(true)
|
||||
.setLocationEnabled(true)
|
||||
.setGeoDetectionEnabled(true)
|
||||
@@ -108,8 +108,8 @@ public class ConfigurationInternalTest {
|
||||
public void test_restricted() {
|
||||
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
|
||||
.setUserConfigAllowed(false)
|
||||
.setAutoDetectionSupported(true)
|
||||
.setGeoDetectionSupported(true)
|
||||
.setAutoDetectionFeatureSupported(true)
|
||||
.setGeoDetectionFeatureSupported(true)
|
||||
.setAutoDetectionEnabled(true)
|
||||
.setLocationEnabled(true)
|
||||
.setGeoDetectionEnabled(true)
|
||||
@@ -170,8 +170,8 @@ public class ConfigurationInternalTest {
|
||||
public void test_autoDetectNotSupported() {
|
||||
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
|
||||
.setUserConfigAllowed(true)
|
||||
.setAutoDetectionSupported(false)
|
||||
.setGeoDetectionSupported(false)
|
||||
.setAutoDetectionFeatureSupported(false)
|
||||
.setGeoDetectionFeatureSupported(false)
|
||||
.setAutoDetectionEnabled(true)
|
||||
.setLocationEnabled(true)
|
||||
.setGeoDetectionEnabled(true)
|
||||
@@ -232,8 +232,8 @@ public class ConfigurationInternalTest {
|
||||
public void test_geoDetectNotSupported() {
|
||||
ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
|
||||
.setUserConfigAllowed(true)
|
||||
.setAutoDetectionSupported(true)
|
||||
.setGeoDetectionSupported(false)
|
||||
.setAutoDetectionFeatureSupported(true)
|
||||
.setGeoDetectionFeatureSupported(false)
|
||||
.setAutoDetectionEnabled(true)
|
||||
.setLocationEnabled(true)
|
||||
.setGeoDetectionEnabled(true)
|
||||
|
||||
@@ -364,8 +364,8 @@ public class TimeZoneDetectorServiceTest {
|
||||
// the tests.
|
||||
final boolean geoDetectionEnabled = autoDetectionEnabled;
|
||||
return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
|
||||
.setAutoDetectionSupported(true)
|
||||
.setGeoDetectionSupported(true)
|
||||
.setAutoDetectionFeatureSupported(true)
|
||||
.setGeoDetectionFeatureSupported(true)
|
||||
.setUserConfigAllowed(true)
|
||||
.setAutoDetectionEnabled(autoDetectionEnabled)
|
||||
.setLocationEnabled(geoDetectionEnabled)
|
||||
|
||||
@@ -90,8 +90,8 @@ public class TimeZoneDetectorStrategyImplTest {
|
||||
private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_DISABLED =
|
||||
new ConfigurationInternal.Builder(USER_ID)
|
||||
.setUserConfigAllowed(false)
|
||||
.setAutoDetectionSupported(true)
|
||||
.setGeoDetectionSupported(true)
|
||||
.setAutoDetectionFeatureSupported(true)
|
||||
.setGeoDetectionFeatureSupported(true)
|
||||
.setAutoDetectionEnabled(false)
|
||||
.setLocationEnabled(true)
|
||||
.setGeoDetectionEnabled(false)
|
||||
@@ -100,8 +100,8 @@ public class TimeZoneDetectorStrategyImplTest {
|
||||
private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED =
|
||||
new ConfigurationInternal.Builder(USER_ID)
|
||||
.setUserConfigAllowed(false)
|
||||
.setAutoDetectionSupported(true)
|
||||
.setGeoDetectionSupported(true)
|
||||
.setAutoDetectionFeatureSupported(true)
|
||||
.setGeoDetectionFeatureSupported(true)
|
||||
.setAutoDetectionEnabled(true)
|
||||
.setLocationEnabled(true)
|
||||
.setGeoDetectionEnabled(true)
|
||||
@@ -110,8 +110,8 @@ public class TimeZoneDetectorStrategyImplTest {
|
||||
private static final ConfigurationInternal CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED =
|
||||
new ConfigurationInternal.Builder(USER_ID)
|
||||
.setUserConfigAllowed(true)
|
||||
.setAutoDetectionSupported(false)
|
||||
.setGeoDetectionSupported(false)
|
||||
.setAutoDetectionFeatureSupported(false)
|
||||
.setGeoDetectionFeatureSupported(false)
|
||||
.setAutoDetectionEnabled(false)
|
||||
.setLocationEnabled(true)
|
||||
.setGeoDetectionEnabled(false)
|
||||
@@ -120,8 +120,8 @@ public class TimeZoneDetectorStrategyImplTest {
|
||||
private static final ConfigurationInternal CONFIG_INT_AUTO_SUPPORTED_GEO_NOT_SUPPORTED =
|
||||
new ConfigurationInternal.Builder(USER_ID)
|
||||
.setUserConfigAllowed(true)
|
||||
.setAutoDetectionSupported(true)
|
||||
.setGeoDetectionSupported(false)
|
||||
.setAutoDetectionFeatureSupported(true)
|
||||
.setGeoDetectionFeatureSupported(false)
|
||||
.setAutoDetectionEnabled(true)
|
||||
.setLocationEnabled(true)
|
||||
.setGeoDetectionEnabled(true)
|
||||
@@ -130,8 +130,8 @@ public class TimeZoneDetectorStrategyImplTest {
|
||||
private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_DISABLED =
|
||||
new ConfigurationInternal.Builder(USER_ID)
|
||||
.setUserConfigAllowed(true)
|
||||
.setAutoDetectionSupported(true)
|
||||
.setGeoDetectionSupported(true)
|
||||
.setAutoDetectionFeatureSupported(true)
|
||||
.setGeoDetectionFeatureSupported(true)
|
||||
.setAutoDetectionEnabled(false)
|
||||
.setLocationEnabled(true)
|
||||
.setGeoDetectionEnabled(false)
|
||||
@@ -139,8 +139,8 @@ public class TimeZoneDetectorStrategyImplTest {
|
||||
|
||||
private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_DISABLED =
|
||||
new ConfigurationInternal.Builder(USER_ID)
|
||||
.setAutoDetectionSupported(true)
|
||||
.setGeoDetectionSupported(true)
|
||||
.setAutoDetectionFeatureSupported(true)
|
||||
.setGeoDetectionFeatureSupported(true)
|
||||
.setUserConfigAllowed(true)
|
||||
.setAutoDetectionEnabled(true)
|
||||
.setLocationEnabled(true)
|
||||
@@ -149,8 +149,8 @@ public class TimeZoneDetectorStrategyImplTest {
|
||||
|
||||
private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_ENABLED =
|
||||
new ConfigurationInternal.Builder(USER_ID)
|
||||
.setAutoDetectionSupported(true)
|
||||
.setGeoDetectionSupported(true)
|
||||
.setAutoDetectionFeatureSupported(true)
|
||||
.setGeoDetectionFeatureSupported(true)
|
||||
.setUserConfigAllowed(true)
|
||||
.setAutoDetectionEnabled(true)
|
||||
.setLocationEnabled(true)
|
||||
|
||||
@@ -44,8 +44,8 @@ final class TestSupport {
|
||||
@UserIdInt int userId, boolean geoDetectionEnabled) {
|
||||
return new ConfigurationInternal.Builder(userId)
|
||||
.setUserConfigAllowed(true)
|
||||
.setAutoDetectionSupported(true)
|
||||
.setGeoDetectionSupported(true)
|
||||
.setAutoDetectionFeatureSupported(true)
|
||||
.setGeoDetectionFeatureSupported(true)
|
||||
.setAutoDetectionEnabled(true)
|
||||
.setLocationEnabled(true)
|
||||
.setGeoDetectionEnabled(geoDetectionEnabled)
|
||||
|
||||
Reference in New Issue
Block a user