blob: 2291777651dd8269676342f9f612933e90feeb3b [file] [log] [blame]
/*
* Copyright (C) 2020 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 static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED;
import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE;
import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED;
import static android.app.time.Capabilities.CAPABILITY_POSSESSED;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.time.TimeZoneCapabilities;
import android.app.time.TimeZoneCapabilitiesAndConfig;
import android.app.time.TimeZoneConfiguration;
import android.os.UserHandle;
import java.util.Objects;
/**
* 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 boolean mTelephonyDetectionSupported;
private final boolean mGeoDetectionSupported;
private final boolean mTelephonyFallbackSupported;
private final boolean mEnhancedMetricsCollectionEnabled;
private final boolean mAutoDetectionEnabledSetting;
private final @UserIdInt int mUserId;
private final boolean mUserConfigAllowed;
private final boolean mLocationEnabledSetting;
private final boolean mGeoDetectionEnabledSetting;
private ConfigurationInternal(Builder builder) {
mTelephonyDetectionSupported = builder.mTelephonyDetectionSupported;
mGeoDetectionSupported = builder.mGeoDetectionSupported;
mTelephonyFallbackSupported = builder.mTelephonyFallbackSupported;
mEnhancedMetricsCollectionEnabled = builder.mEnhancedMetricsCollectionEnabled;
mAutoDetectionEnabledSetting = builder.mAutoDetectionEnabledSetting;
mUserId = builder.mUserId;
mUserConfigAllowed = builder.mUserConfigAllowed;
mLocationEnabledSetting = builder.mLocationEnabledSetting;
mGeoDetectionEnabledSetting = builder.mGeoDetectionEnabledSetting;
}
/** Returns true if the device supports any form of auto time zone detection. */
public boolean isAutoDetectionSupported() {
return mTelephonyDetectionSupported || mGeoDetectionSupported;
}
/** Returns true if the device supports telephony time zone detection. */
public boolean isTelephonyDetectionSupported() {
return mTelephonyDetectionSupported;
}
/** Returns true if the device supports geolocation time zone detection. */
public boolean isGeoDetectionSupported() {
return mGeoDetectionSupported;
}
/**
* Returns true if the device supports time zone detection falling back to telephony detection
* under certain circumstances.
*/
public boolean isTelephonyFallbackSupported() {
return mTelephonyFallbackSupported;
}
/**
* Returns {@code true} if the device can collect / report extra metrics information for QA
* / testers. These metrics might involve logging more expensive or more revealing data that
* would not be collected from the set of public users.
*/
public boolean isEnhancedMetricsCollectionEnabled() {
return mEnhancedMetricsCollectionEnabled;
}
/** Returns the value of the auto time zone detection enabled setting. */
public boolean getAutoDetectionEnabledSetting() {
return mAutoDetectionEnabledSetting;
}
/**
* Returns true if auto time zone detection behavior is actually enabled, which can be distinct
* from the raw setting value.
*/
public boolean getAutoDetectionEnabledBehavior() {
return isAutoDetectionSupported() && mAutoDetectionEnabledSetting;
}
/** 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 getLocationEnabledSetting() {
return mLocationEnabledSetting;
}
/** Returns the value of the geolocation time zone detection enabled setting. */
public boolean getGeoDetectionEnabledSetting() {
return mGeoDetectionEnabledSetting;
}
/**
* Returns true if geolocation time zone detection behavior is actually enabled, which can be
* distinct from the raw setting value.
*/
public boolean getGeoDetectionEnabledBehavior() {
return getAutoDetectionEnabledBehavior()
&& isGeoDetectionSupported()
&& getLocationEnabledSetting()
&& getGeoDetectionEnabledSetting();
}
/** Creates a {@link TimeZoneCapabilitiesAndConfig} object using the configuration values. */
public TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig() {
return new TimeZoneCapabilitiesAndConfig(asCapabilities(), asConfiguration());
}
@NonNull
private TimeZoneCapabilities asCapabilities() {
UserHandle userHandle = UserHandle.of(mUserId);
TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(userHandle);
boolean allowConfigDateTime = isUserConfigAllowed();
// Automatic time zone detection is only supported on devices if there is a telephony
// network available or geolocation time zone detection is possible.
boolean deviceHasAutoTimeZoneDetection = isAutoDetectionSupported();
final int configureAutoDetectionEnabledCapability;
if (!deviceHasAutoTimeZoneDetection) {
configureAutoDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED;
} else if (!allowConfigDateTime) {
configureAutoDetectionEnabledCapability = CAPABILITY_NOT_ALLOWED;
} else {
configureAutoDetectionEnabledCapability = CAPABILITY_POSSESSED;
}
builder.setConfigureAutoDetectionEnabledCapability(configureAutoDetectionEnabledCapability);
boolean deviceHasLocationTimeZoneDetection = isGeoDetectionSupported();
// Note: allowConfigDateTime does not restrict the ability to change location time zone
// detection enabled. This is intentional as it has user privacy implications and so it
// makes sense to leave this under a user's control.
final int configureGeolocationDetectionEnabledCapability;
if (!deviceHasLocationTimeZoneDetection) {
configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED;
} else if (!mAutoDetectionEnabledSetting || !getLocationEnabledSetting()) {
configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_APPLICABLE;
} else {
configureGeolocationDetectionEnabledCapability = CAPABILITY_POSSESSED;
}
builder.setConfigureGeoDetectionEnabledCapability(
configureGeolocationDetectionEnabledCapability);
// The ability to make manual time zone suggestions can also be restricted by policy. With
// the current logic above, this could lead to a situation where a device hardware does not
// support auto detection, the device has been forced into "auto" mode by an admin and the
// user is unable to disable auto detection.
final int suggestManualTimeZoneCapability;
if (!allowConfigDateTime) {
suggestManualTimeZoneCapability = CAPABILITY_NOT_ALLOWED;
} else if (getAutoDetectionEnabledBehavior()) {
suggestManualTimeZoneCapability = CAPABILITY_NOT_APPLICABLE;
} else {
suggestManualTimeZoneCapability = CAPABILITY_POSSESSED;
}
builder.setSuggestManualTimeZoneCapability(suggestManualTimeZoneCapability);
return builder.build();
}
/** Returns a {@link TimeZoneConfiguration} from the configuration values. */
private TimeZoneConfiguration asConfiguration() {
return new TimeZoneConfiguration.Builder()
.setAutoDetectionEnabled(getAutoDetectionEnabledSetting())
.setGeoDetectionEnabled(getGeoDetectionEnabledSetting())
.build();
}
/**
* Merges the configuration values from this with any properties set in {@code
* newConfiguration}. The new configuration has precedence. Used to apply user updates to
* internal configuration.
*/
public ConfigurationInternal merge(TimeZoneConfiguration newConfiguration) {
Builder builder = new Builder(this);
if (newConfiguration.hasIsAutoDetectionEnabled()) {
builder.setAutoDetectionEnabledSetting(newConfiguration.isAutoDetectionEnabled());
}
if (newConfiguration.hasIsGeoDetectionEnabled()) {
builder.setGeoDetectionEnabledSetting(newConfiguration.isGeoDetectionEnabled());
}
return builder.build();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ConfigurationInternal that = (ConfigurationInternal) o;
return mUserId == that.mUserId
&& mUserConfigAllowed == that.mUserConfigAllowed
&& mTelephonyDetectionSupported == that.mTelephonyDetectionSupported
&& mGeoDetectionSupported == that.mGeoDetectionSupported
&& mTelephonyFallbackSupported == that.mTelephonyFallbackSupported
&& mEnhancedMetricsCollectionEnabled == that.mEnhancedMetricsCollectionEnabled
&& mAutoDetectionEnabledSetting == that.mAutoDetectionEnabledSetting
&& mLocationEnabledSetting == that.mLocationEnabledSetting
&& mGeoDetectionEnabledSetting == that.mGeoDetectionEnabledSetting;
}
@Override
public int hashCode() {
return Objects.hash(mUserId, mUserConfigAllowed, mTelephonyDetectionSupported,
mGeoDetectionSupported, mTelephonyFallbackSupported,
mEnhancedMetricsCollectionEnabled, mAutoDetectionEnabledSetting,
mLocationEnabledSetting, mGeoDetectionEnabledSetting);
}
@Override
public String toString() {
return "ConfigurationInternal{"
+ "mUserId=" + mUserId
+ ", mUserConfigAllowed=" + mUserConfigAllowed
+ ", mTelephonyDetectionSupported=" + mTelephonyDetectionSupported
+ ", mGeoDetectionSupported=" + mGeoDetectionSupported
+ ", mTelephonyFallbackSupported=" + mTelephonyFallbackSupported
+ ", mEnhancedMetricsCollectionEnabled=" + mEnhancedMetricsCollectionEnabled
+ ", mAutoDetectionEnabledSetting=" + mAutoDetectionEnabledSetting
+ ", mLocationEnabledSetting=" + mLocationEnabledSetting
+ ", mGeoDetectionEnabledSetting=" + mGeoDetectionEnabledSetting
+ '}';
}
/**
* A Builder for {@link ConfigurationInternal}.
*/
public static class Builder {
private final @UserIdInt int mUserId;
private boolean mUserConfigAllowed;
private boolean mTelephonyDetectionSupported;
private boolean mGeoDetectionSupported;
private boolean mTelephonyFallbackSupported;
private boolean mEnhancedMetricsCollectionEnabled;
private boolean mAutoDetectionEnabledSetting;
private boolean mLocationEnabledSetting;
private boolean mGeoDetectionEnabledSetting;
/**
* Creates a new Builder with only the userId set.
*/
public Builder(@UserIdInt int userId) {
mUserId = userId;
}
/**
* Creates a new Builder by copying values from an existing instance.
*/
public Builder(ConfigurationInternal toCopy) {
this.mUserId = toCopy.mUserId;
this.mUserConfigAllowed = toCopy.mUserConfigAllowed;
this.mTelephonyDetectionSupported = toCopy.mTelephonyDetectionSupported;
this.mTelephonyFallbackSupported = toCopy.mTelephonyFallbackSupported;
this.mGeoDetectionSupported = toCopy.mGeoDetectionSupported;
this.mEnhancedMetricsCollectionEnabled = toCopy.mEnhancedMetricsCollectionEnabled;
this.mAutoDetectionEnabledSetting = toCopy.mAutoDetectionEnabledSetting;
this.mLocationEnabledSetting = toCopy.mLocationEnabledSetting;
this.mGeoDetectionEnabledSetting = toCopy.mGeoDetectionEnabledSetting;
}
/**
* Sets whether the user is allowed to configure time zone settings on this device.
*/
public Builder setUserConfigAllowed(boolean configAllowed) {
mUserConfigAllowed = configAllowed;
return this;
}
/**
* Sets whether telephony time zone detection is supported on this device.
*/
public Builder setTelephonyDetectionFeatureSupported(boolean supported) {
mTelephonyDetectionSupported = supported;
return this;
}
/**
* Sets whether geolocation time zone detection is supported on this device.
*/
public Builder setGeoDetectionFeatureSupported(boolean supported) {
mGeoDetectionSupported = supported;
return this;
}
/**
* Sets whether time zone detection supports falling back to telephony detection under
* certain circumstances.
*/
public Builder setTelephonyFallbackSupported(boolean supported) {
mTelephonyFallbackSupported = supported;
return this;
}
/**
* Sets the value for enhanced metrics collection.
*/
public Builder setEnhancedMetricsCollectionEnabled(boolean enabled) {
mEnhancedMetricsCollectionEnabled = enabled;
return this;
}
/**
* Sets the value of the automatic time zone detection enabled setting for this device.
*/
public Builder setAutoDetectionEnabledSetting(boolean enabled) {
mAutoDetectionEnabledSetting = enabled;
return this;
}
/**
* Sets the value of the location mode setting for this user.
*/
public Builder setLocationEnabledSetting(boolean enabled) {
mLocationEnabledSetting = enabled;
return this;
}
/**
* Sets the value of the geolocation time zone detection setting for this user.
*/
public Builder setGeoDetectionEnabledSetting(boolean enabled) {
mGeoDetectionEnabledSetting = enabled;
return this;
}
/** Returns a new {@link ConfigurationInternal}. */
@NonNull
public ConfigurationInternal build() {
return new ConfigurationInternal(this);
}
}
}