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 android.app.time;
18 
19 import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SystemApi;
24 import android.app.time.Capabilities.CapabilityState;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 import android.os.UserHandle;
28 
29 import java.util.Objects;
30 
31 /**
32  * Time zone-related capabilities for a user.
33  *
34  * <p>For configuration settings capabilities, the associated settings value can be found via
35  * {@link TimeManager#getTimeZoneCapabilitiesAndConfig()} and may be changed using {@link
36  * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)} (if the user's capabilities
37  * allow).
38  *
39  * @hide
40  */
41 @SystemApi
42 public final class TimeZoneCapabilities implements Parcelable {
43 
44     public static final @NonNull Creator<TimeZoneCapabilities> CREATOR = new Creator<>() {
45         public TimeZoneCapabilities createFromParcel(Parcel in) {
46             return TimeZoneCapabilities.createFromParcel(in);
47         }
48 
49         public TimeZoneCapabilities[] newArray(int size) {
50             return new TimeZoneCapabilities[size];
51         }
52     };
53 
54     /**
55      * The user the capabilities are for. This is used for object equality and debugging but there
56      * is no accessor.
57      */
58     @NonNull private final UserHandle mUserHandle;
59     private final @CapabilityState int mConfigureAutoDetectionEnabledCapability;
60 
61     /**
62      * The values of the user's "Use location" value, AKA the Master Location Switch.
63      *
64      * <p>This is only exposed for SettingsUI and so is not part of the SDK API.
65      *
66      * <p>This is not treated as a CapabilityState as it's a boolean value that all user's have.
67      */
68     private final boolean mUseLocationEnabled;
69 
70     private final @CapabilityState int mConfigureGeoDetectionEnabledCapability;
71     private final @CapabilityState int mSetManualTimeZoneCapability;
72 
TimeZoneCapabilities(@onNull Builder builder)73     private TimeZoneCapabilities(@NonNull Builder builder) {
74         this.mUserHandle = Objects.requireNonNull(builder.mUserHandle);
75         this.mConfigureAutoDetectionEnabledCapability =
76                 builder.mConfigureAutoDetectionEnabledCapability;
77         this.mUseLocationEnabled = builder.mUseLocationEnabled;
78         this.mConfigureGeoDetectionEnabledCapability =
79                 builder.mConfigureGeoDetectionEnabledCapability;
80         this.mSetManualTimeZoneCapability = builder.mSetManualTimeZoneCapability;
81     }
82 
83     @NonNull
createFromParcel(@onNull Parcel in)84     private static TimeZoneCapabilities createFromParcel(@NonNull Parcel in) {
85         UserHandle userHandle = UserHandle.readFromParcel(in);
86         return new TimeZoneCapabilities.Builder(userHandle)
87                 .setConfigureAutoDetectionEnabledCapability(in.readInt())
88                 .setUseLocationEnabled(in.readBoolean())
89                 .setConfigureGeoDetectionEnabledCapability(in.readInt())
90                 .setSetManualTimeZoneCapability(in.readInt())
91                 .build();
92     }
93 
94     @Override
writeToParcel(@onNull Parcel dest, int flags)95     public void writeToParcel(@NonNull Parcel dest, int flags) {
96         UserHandle.writeToParcel(mUserHandle, dest);
97         dest.writeInt(mConfigureAutoDetectionEnabledCapability);
98         dest.writeBoolean(mUseLocationEnabled);
99         dest.writeInt(mConfigureGeoDetectionEnabledCapability);
100         dest.writeInt(mSetManualTimeZoneCapability);
101     }
102 
103     /**
104      * Returns the capability state associated with the user's ability to modify the automatic time
105      * zone detection setting. The setting can be updated via {@link
106      * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)}.
107      */
108     @CapabilityState
getConfigureAutoDetectionEnabledCapability()109     public int getConfigureAutoDetectionEnabledCapability() {
110         return mConfigureAutoDetectionEnabledCapability;
111     }
112 
113     /**
114      * Returns {@code true} if the device's location can be used by the Android system, and
115      * therefore the platform components running on behalf of the user. At the time of writing, the
116      * user can change this via the "Use location" setting on the Location settings screen.
117      *
118      * Not part of the SDK API because it is intended for use by SettingsUI, which can display
119      * text about needing it to be on for location-based time zone detection.
120      * @hide
121      *
122      */
isUseLocationEnabled()123     public boolean isUseLocationEnabled() {
124         return mUseLocationEnabled;
125     }
126 
127     /**
128      * Returns the capability state associated with the user's ability to modify the geolocation
129      * detection setting. The setting can be updated via {@link
130      * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)}.
131      */
132     @CapabilityState
getConfigureGeoDetectionEnabledCapability()133     public int getConfigureGeoDetectionEnabledCapability() {
134         return mConfigureGeoDetectionEnabledCapability;
135     }
136 
137     /**
138      * Returns the capability state associated with the user's ability to manually set the time zone
139      * on a device.
140      *
141      * <p>The time zone will be ignored in all cases unless the value is {@link
142      * Capabilities#CAPABILITY_POSSESSED}. See also
143      * {@link TimeZoneConfiguration#isAutoDetectionEnabled()}.
144      */
145     @CapabilityState
getSetManualTimeZoneCapability()146     public int getSetManualTimeZoneCapability() {
147         return mSetManualTimeZoneCapability;
148     }
149 
150     /**
151      * Tries to create a new {@link TimeZoneConfiguration} from the {@code config} and the set of
152      * {@code requestedChanges}, if {@code this} capabilities allow. The new configuration is
153      * returned. If the capabilities do not permit one or more of the requested changes then {@code
154      * null} is returned.
155      *
156      * @hide
157      */
158     @Nullable
tryApplyConfigChanges( @onNull TimeZoneConfiguration config, @NonNull TimeZoneConfiguration requestedChanges)159     public TimeZoneConfiguration tryApplyConfigChanges(
160             @NonNull TimeZoneConfiguration config,
161             @NonNull TimeZoneConfiguration requestedChanges) {
162         TimeZoneConfiguration.Builder newConfigBuilder = new TimeZoneConfiguration.Builder(config);
163         if (requestedChanges.hasIsAutoDetectionEnabled()) {
164             if (this.getConfigureAutoDetectionEnabledCapability() < CAPABILITY_NOT_APPLICABLE) {
165                 return null;
166             }
167             newConfigBuilder.setAutoDetectionEnabled(requestedChanges.isAutoDetectionEnabled());
168         }
169 
170         if (requestedChanges.hasIsGeoDetectionEnabled()) {
171             if (this.getConfigureGeoDetectionEnabledCapability() < CAPABILITY_NOT_APPLICABLE) {
172                 return null;
173             }
174             newConfigBuilder.setGeoDetectionEnabled(requestedChanges.isGeoDetectionEnabled());
175         }
176 
177         return newConfigBuilder.build();
178     }
179 
180     @Override
describeContents()181     public int describeContents() {
182         return 0;
183     }
184 
185     @Override
equals(@ullable Object o)186     public boolean equals(@Nullable Object o) {
187         if (this == o) {
188             return true;
189         }
190         if (o == null || getClass() != o.getClass()) {
191             return false;
192         }
193         TimeZoneCapabilities that = (TimeZoneCapabilities) o;
194         return mUserHandle.equals(that.mUserHandle)
195                 && mConfigureAutoDetectionEnabledCapability
196                 == that.mConfigureAutoDetectionEnabledCapability
197                 && mUseLocationEnabled == that.mUseLocationEnabled
198                 && mConfigureGeoDetectionEnabledCapability
199                 == that.mConfigureGeoDetectionEnabledCapability
200                 && mSetManualTimeZoneCapability == that.mSetManualTimeZoneCapability;
201     }
202 
203     @Override
hashCode()204     public int hashCode() {
205         return Objects.hash(mUserHandle, mConfigureAutoDetectionEnabledCapability,
206                 mConfigureGeoDetectionEnabledCapability, mSetManualTimeZoneCapability);
207     }
208 
209     @Override
toString()210     public String toString() {
211         return "TimeZoneDetectorCapabilities{"
212                 + "mUserHandle=" + mUserHandle
213                 + ", mConfigureAutoDetectionEnabledCapability="
214                 + mConfigureAutoDetectionEnabledCapability
215                 + ", mUseLocationEnabled=" + mUseLocationEnabled
216                 + ", mConfigureGeoDetectionEnabledCapability="
217                 + mConfigureGeoDetectionEnabledCapability
218                 + ", mSetManualTimeZoneCapability=" + mSetManualTimeZoneCapability
219                 + '}';
220     }
221 
222     /**
223      * A builder of {@link TimeZoneCapabilities} objects.
224      *
225      * @hide
226      */
227     public static class Builder {
228 
229         @NonNull private UserHandle mUserHandle;
230         private @CapabilityState int mConfigureAutoDetectionEnabledCapability;
231         private Boolean mUseLocationEnabled;
232         private @CapabilityState int mConfigureGeoDetectionEnabledCapability;
233         private @CapabilityState int mSetManualTimeZoneCapability;
234 
Builder(@onNull UserHandle userHandle)235         public Builder(@NonNull UserHandle userHandle) {
236             mUserHandle = Objects.requireNonNull(userHandle);
237         }
238 
Builder(@onNull TimeZoneCapabilities capabilitiesToCopy)239         public Builder(@NonNull TimeZoneCapabilities capabilitiesToCopy) {
240             Objects.requireNonNull(capabilitiesToCopy);
241             mUserHandle = capabilitiesToCopy.mUserHandle;
242             mConfigureAutoDetectionEnabledCapability =
243                 capabilitiesToCopy.mConfigureAutoDetectionEnabledCapability;
244             mUseLocationEnabled = capabilitiesToCopy.mUseLocationEnabled;
245             mConfigureGeoDetectionEnabledCapability =
246                 capabilitiesToCopy.mConfigureGeoDetectionEnabledCapability;
247             mSetManualTimeZoneCapability =
248                 capabilitiesToCopy.mSetManualTimeZoneCapability;
249         }
250 
251         /** Sets the value for the "configure automatic time zone detection enabled" capability. */
setConfigureAutoDetectionEnabledCapability(@apabilityState int value)252         public Builder setConfigureAutoDetectionEnabledCapability(@CapabilityState int value) {
253             this.mConfigureAutoDetectionEnabledCapability = value;
254             return this;
255         }
256 
257         /** Sets the values for "use location". See {@link #isUseLocationEnabled()}. */
setUseLocationEnabled(boolean useLocation)258         public Builder setUseLocationEnabled(boolean useLocation) {
259             mUseLocationEnabled = useLocation;
260             return this;
261         }
262 
263         /**
264          * Sets the value for the "configure geolocation time zone detection enabled" capability.
265          */
setConfigureGeoDetectionEnabledCapability(@apabilityState int value)266         public Builder setConfigureGeoDetectionEnabledCapability(@CapabilityState int value) {
267             this.mConfigureGeoDetectionEnabledCapability = value;
268             return this;
269         }
270 
271         /** Sets the value for the "set manual time zone" capability. */
setSetManualTimeZoneCapability(@apabilityState int value)272         public Builder setSetManualTimeZoneCapability(@CapabilityState int value) {
273             this.mSetManualTimeZoneCapability = value;
274             return this;
275         }
276 
277         /** Returns the {@link TimeZoneCapabilities}. */
278         @NonNull
build()279         public TimeZoneCapabilities build() {
280             verifyCapabilitySet(mConfigureAutoDetectionEnabledCapability,
281                     "configureAutoDetectionEnabledCapability");
282             Objects.requireNonNull(mUseLocationEnabled, "useLocationEnabled");
283             verifyCapabilitySet(mConfigureGeoDetectionEnabledCapability,
284                     "configureGeoDetectionEnabledCapability");
285             verifyCapabilitySet(mSetManualTimeZoneCapability,
286                     "mSetManualTimeZoneCapability");
287             return new TimeZoneCapabilities(this);
288         }
289 
verifyCapabilitySet(int value, String name)290         private void verifyCapabilitySet(int value, String name) {
291             if (value == 0) {
292                 throw new IllegalStateException(name + " not set");
293             }
294         }
295     }
296 }
297