1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.timezonedetector; 18 19 import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED; 20 import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE; 21 import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED; 22 import static android.app.time.Capabilities.CAPABILITY_POSSESSED; 23 24 import android.annotation.NonNull; 25 import android.annotation.UserIdInt; 26 import android.app.time.TimeZoneCapabilities; 27 import android.app.time.TimeZoneCapabilitiesAndConfig; 28 import android.app.time.TimeZoneConfiguration; 29 import android.os.UserHandle; 30 31 import java.util.Objects; 32 33 /** 34 * Holds configuration values that affect user-facing time zone behavior and some associated logic. 35 * Some configuration is global, some is user scoped, but this class deliberately doesn't make a 36 * distinction for simplicity. 37 */ 38 public final class ConfigurationInternal { 39 40 private final boolean mTelephonyDetectionSupported; 41 private final boolean mGeoDetectionSupported; 42 private final boolean mAutoDetectionEnabled; 43 private final @UserIdInt int mUserId; 44 private final boolean mUserConfigAllowed; 45 private final boolean mLocationEnabled; 46 private final boolean mGeoDetectionEnabled; 47 ConfigurationInternal(Builder builder)48 private ConfigurationInternal(Builder builder) { 49 mTelephonyDetectionSupported = builder.mTelephonyDetectionSupported; 50 mGeoDetectionSupported = builder.mGeoDetectionSupported; 51 mAutoDetectionEnabled = builder.mAutoDetectionEnabled; 52 53 mUserId = builder.mUserId; 54 mUserConfigAllowed = builder.mUserConfigAllowed; 55 mLocationEnabled = builder.mLocationEnabled; 56 mGeoDetectionEnabled = builder.mGeoDetectionEnabled; 57 } 58 59 /** Returns true if the device supports any form of auto time zone detection. */ isAutoDetectionSupported()60 public boolean isAutoDetectionSupported() { 61 return mTelephonyDetectionSupported || mGeoDetectionSupported; 62 } 63 64 /** Returns true if the device supports telephony time zone detection. */ isTelephonyDetectionSupported()65 public boolean isTelephonyDetectionSupported() { 66 return mTelephonyDetectionSupported; 67 } 68 69 /** Returns true if the device supports geolocation time zone detection. */ isGeoDetectionSupported()70 public boolean isGeoDetectionSupported() { 71 return mGeoDetectionSupported; 72 } 73 74 /** Returns the value of the auto time zone detection enabled setting. */ getAutoDetectionEnabledSetting()75 public boolean getAutoDetectionEnabledSetting() { 76 return mAutoDetectionEnabled; 77 } 78 79 /** 80 * Returns true if auto time zone detection behavior is actually enabled, which can be distinct 81 * from the raw setting value. 82 */ getAutoDetectionEnabledBehavior()83 public boolean getAutoDetectionEnabledBehavior() { 84 return isAutoDetectionSupported() && mAutoDetectionEnabled; 85 } 86 87 /** Returns the ID of the user this configuration is associated with. */ getUserId()88 public @UserIdInt int getUserId() { 89 return mUserId; 90 } 91 92 /** Returns the handle of the user this configuration is associated with. */ 93 @NonNull getUserHandle()94 public UserHandle getUserHandle() { 95 return UserHandle.of(mUserId); 96 } 97 98 /** Returns true if the user allowed to modify time zone configuration. */ isUserConfigAllowed()99 public boolean isUserConfigAllowed() { 100 return mUserConfigAllowed; 101 } 102 103 /** Returns true if user's location can be used generally. */ isLocationEnabled()104 public boolean isLocationEnabled() { 105 return mLocationEnabled; 106 } 107 108 /** Returns the value of the geolocation time zone detection enabled setting. */ getGeoDetectionEnabledSetting()109 public boolean getGeoDetectionEnabledSetting() { 110 return mGeoDetectionEnabled; 111 } 112 113 /** 114 * Returns true if geolocation time zone detection behavior is actually enabled, which can be 115 * distinct from the raw setting value. 116 */ getGeoDetectionEnabledBehavior()117 public boolean getGeoDetectionEnabledBehavior() { 118 return getAutoDetectionEnabledBehavior() 119 && isGeoDetectionSupported() 120 && isLocationEnabled() 121 && getGeoDetectionEnabledSetting(); 122 } 123 124 /** Creates a {@link TimeZoneCapabilitiesAndConfig} object using the configuration values. */ createCapabilitiesAndConfig()125 public TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig() { 126 return new TimeZoneCapabilitiesAndConfig(asCapabilities(), asConfiguration()); 127 } 128 129 @NonNull asCapabilities()130 private TimeZoneCapabilities asCapabilities() { 131 UserHandle userHandle = UserHandle.of(mUserId); 132 TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(userHandle); 133 134 boolean allowConfigDateTime = isUserConfigAllowed(); 135 136 // Automatic time zone detection is only supported on devices if there is a telephony 137 // network available or geolocation time zone detection is possible. 138 boolean deviceHasAutoTimeZoneDetection = isAutoDetectionSupported(); 139 140 final int configureAutoDetectionEnabledCapability; 141 if (!deviceHasAutoTimeZoneDetection) { 142 configureAutoDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED; 143 } else if (!allowConfigDateTime) { 144 configureAutoDetectionEnabledCapability = CAPABILITY_NOT_ALLOWED; 145 } else { 146 configureAutoDetectionEnabledCapability = CAPABILITY_POSSESSED; 147 } 148 builder.setConfigureAutoDetectionEnabledCapability(configureAutoDetectionEnabledCapability); 149 150 boolean deviceHasLocationTimeZoneDetection = isGeoDetectionSupported(); 151 // Note: allowConfigDateTime does not restrict the ability to change location time zone 152 // detection enabled. This is intentional as it has user privacy implications and so it 153 // makes sense to leave this under a user's control. 154 final int configureGeolocationDetectionEnabledCapability; 155 if (!deviceHasLocationTimeZoneDetection) { 156 configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED; 157 } else if (!mAutoDetectionEnabled || !isLocationEnabled()) { 158 configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_APPLICABLE; 159 } else { 160 configureGeolocationDetectionEnabledCapability = CAPABILITY_POSSESSED; 161 } 162 builder.setConfigureGeoDetectionEnabledCapability( 163 configureGeolocationDetectionEnabledCapability); 164 165 // The ability to make manual time zone suggestions can also be restricted by policy. With 166 // the current logic above, this could lead to a situation where a device hardware does not 167 // support auto detection, the device has been forced into "auto" mode by an admin and the 168 // user is unable to disable auto detection. 169 final int suggestManualTimeZoneCapability; 170 if (!allowConfigDateTime) { 171 suggestManualTimeZoneCapability = CAPABILITY_NOT_ALLOWED; 172 } else if (getAutoDetectionEnabledBehavior()) { 173 suggestManualTimeZoneCapability = CAPABILITY_NOT_APPLICABLE; 174 } else { 175 suggestManualTimeZoneCapability = CAPABILITY_POSSESSED; 176 } 177 builder.setSuggestManualTimeZoneCapability(suggestManualTimeZoneCapability); 178 179 return builder.build(); 180 } 181 182 /** Returns a {@link TimeZoneConfiguration} from the configuration values. */ asConfiguration()183 private TimeZoneConfiguration asConfiguration() { 184 return new TimeZoneConfiguration.Builder() 185 .setAutoDetectionEnabled(getAutoDetectionEnabledSetting()) 186 .setGeoDetectionEnabled(getGeoDetectionEnabledSetting()) 187 .build(); 188 } 189 190 /** 191 * Merges the configuration values from this with any properties set in {@code 192 * newConfiguration}. The new configuration has precedence. Used to apply user updates to 193 * internal configuration. 194 */ merge(TimeZoneConfiguration newConfiguration)195 public ConfigurationInternal merge(TimeZoneConfiguration newConfiguration) { 196 Builder builder = new Builder(this); 197 if (newConfiguration.hasIsAutoDetectionEnabled()) { 198 builder.setAutoDetectionEnabled(newConfiguration.isAutoDetectionEnabled()); 199 } 200 if (newConfiguration.hasIsGeoDetectionEnabled()) { 201 builder.setGeoDetectionEnabled(newConfiguration.isGeoDetectionEnabled()); 202 } 203 return builder.build(); 204 } 205 206 @Override equals(Object o)207 public boolean equals(Object o) { 208 if (this == o) { 209 return true; 210 } 211 if (o == null || getClass() != o.getClass()) { 212 return false; 213 } 214 ConfigurationInternal that = (ConfigurationInternal) o; 215 return mUserId == that.mUserId 216 && mUserConfigAllowed == that.mUserConfigAllowed 217 && mTelephonyDetectionSupported == that.mTelephonyDetectionSupported 218 && mGeoDetectionSupported == that.mGeoDetectionSupported 219 && mAutoDetectionEnabled == that.mAutoDetectionEnabled 220 && mLocationEnabled == that.mLocationEnabled 221 && mGeoDetectionEnabled == that.mGeoDetectionEnabled; 222 } 223 224 @Override hashCode()225 public int hashCode() { 226 return Objects.hash(mUserId, mUserConfigAllowed, mTelephonyDetectionSupported, 227 mGeoDetectionSupported, mAutoDetectionEnabled, mLocationEnabled, 228 mGeoDetectionEnabled); 229 } 230 231 @Override toString()232 public String toString() { 233 return "ConfigurationInternal{" 234 + "mUserId=" + mUserId 235 + ", mUserConfigAllowed=" + mUserConfigAllowed 236 + ", mTelephonyDetectionSupported=" + mTelephonyDetectionSupported 237 + ", mGeoDetectionSupported=" + mGeoDetectionSupported 238 + ", mAutoDetectionEnabled=" + mAutoDetectionEnabled 239 + ", mLocationEnabled=" + mLocationEnabled 240 + ", mGeoDetectionEnabled=" + mGeoDetectionEnabled 241 + '}'; 242 } 243 244 /** 245 * A Builder for {@link ConfigurationInternal}. 246 */ 247 public static class Builder { 248 249 private final @UserIdInt int mUserId; 250 251 private boolean mUserConfigAllowed; 252 private boolean mTelephonyDetectionSupported; 253 private boolean mGeoDetectionSupported; 254 private boolean mAutoDetectionEnabled; 255 private boolean mLocationEnabled; 256 private boolean mGeoDetectionEnabled; 257 258 /** 259 * Creates a new Builder with only the userId set. 260 */ Builder(@serIdInt int userId)261 public Builder(@UserIdInt int userId) { 262 mUserId = userId; 263 } 264 265 /** 266 * Creates a new Builder by copying values from an existing instance. 267 */ Builder(ConfigurationInternal toCopy)268 public Builder(ConfigurationInternal toCopy) { 269 this.mUserId = toCopy.mUserId; 270 this.mUserConfigAllowed = toCopy.mUserConfigAllowed; 271 this.mTelephonyDetectionSupported = toCopy.mTelephonyDetectionSupported; 272 this.mGeoDetectionSupported = toCopy.mGeoDetectionSupported; 273 this.mAutoDetectionEnabled = toCopy.mAutoDetectionEnabled; 274 this.mLocationEnabled = toCopy.mLocationEnabled; 275 this.mGeoDetectionEnabled = toCopy.mGeoDetectionEnabled; 276 } 277 278 /** 279 * Sets whether the user is allowed to configure time zone settings on this device. 280 */ setUserConfigAllowed(boolean configAllowed)281 public Builder setUserConfigAllowed(boolean configAllowed) { 282 mUserConfigAllowed = configAllowed; 283 return this; 284 } 285 286 /** 287 * Sets whether telephony time zone detection is supported on this device. 288 */ setTelephonyDetectionFeatureSupported(boolean supported)289 public Builder setTelephonyDetectionFeatureSupported(boolean supported) { 290 mTelephonyDetectionSupported = supported; 291 return this; 292 } 293 294 /** 295 * Sets whether geolocation time zone detection is supported on this device. 296 */ setGeoDetectionFeatureSupported(boolean supported)297 public Builder setGeoDetectionFeatureSupported(boolean supported) { 298 mGeoDetectionSupported = supported; 299 return this; 300 } 301 302 /** 303 * Sets the value of the automatic time zone detection enabled setting for this device. 304 */ setAutoDetectionEnabled(boolean enabled)305 public Builder setAutoDetectionEnabled(boolean enabled) { 306 mAutoDetectionEnabled = enabled; 307 return this; 308 } 309 310 /** 311 * Sets the value of the location mode setting for this user. 312 */ setLocationEnabled(boolean enabled)313 public Builder setLocationEnabled(boolean enabled) { 314 mLocationEnabled = enabled; 315 return this; 316 } 317 318 /** 319 * Sets the value of the geolocation time zone detection setting for this user. 320 */ setGeoDetectionEnabled(boolean enabled)321 public Builder setGeoDetectionEnabled(boolean enabled) { 322 mGeoDetectionEnabled = enabled; 323 return this; 324 } 325 326 /** Returns a new {@link ConfigurationInternal}. */ 327 @NonNull build()328 public ConfigurationInternal build() { 329 return new ConfigurationInternal(this); 330 } 331 } 332 } 333