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.app.timezonedetector.ManualTimeZoneSuggestion;
26 import android.app.timezonedetector.TimeZoneDetector;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 import android.os.UserHandle;
30 
31 import java.util.Objects;
32 
33 /**
34  * Time zone-related capabilities for a user.
35  *
36  * <p>For configuration settings capabilities, the associated settings value can be found via
37  * {@link TimeManager#getTimeZoneCapabilitiesAndConfig()} and may be changed using {@link
38  * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)} (if the user's capabilities
39  * allow).
40  *
41  * @hide
42  */
43 @SystemApi
44 public final class TimeZoneCapabilities implements Parcelable {
45 
46     public static final @NonNull Creator<TimeZoneCapabilities> CREATOR =
47             new Creator<TimeZoneCapabilities>() {
48                 public TimeZoneCapabilities createFromParcel(Parcel in) {
49                     return TimeZoneCapabilities.createFromParcel(in);
50                 }
51 
52                 public TimeZoneCapabilities[] newArray(int size) {
53                     return new TimeZoneCapabilities[size];
54                 }
55             };
56 
57     /**
58      * The user the capabilities are for. This is used for object equality and debugging but there
59      * is no accessor.
60      */
61     @NonNull private final UserHandle mUserHandle;
62     private final @CapabilityState int mConfigureAutoDetectionEnabledCapability;
63     private final @CapabilityState int mConfigureGeoDetectionEnabledCapability;
64     private final @CapabilityState int mSuggestManualTimeZoneCapability;
65 
TimeZoneCapabilities(@onNull Builder builder)66     private TimeZoneCapabilities(@NonNull Builder builder) {
67         this.mUserHandle = Objects.requireNonNull(builder.mUserHandle);
68         this.mConfigureAutoDetectionEnabledCapability =
69                 builder.mConfigureAutoDetectionEnabledCapability;
70         this.mConfigureGeoDetectionEnabledCapability =
71                 builder.mConfigureGeoDetectionEnabledCapability;
72         this.mSuggestManualTimeZoneCapability = builder.mSuggestManualTimeZoneCapability;
73     }
74 
75     @NonNull
createFromParcel(Parcel in)76     private static TimeZoneCapabilities createFromParcel(Parcel in) {
77         UserHandle userHandle = UserHandle.readFromParcel(in);
78         return new TimeZoneCapabilities.Builder(userHandle)
79                 .setConfigureAutoDetectionEnabledCapability(in.readInt())
80                 .setConfigureGeoDetectionEnabledCapability(in.readInt())
81                 .setSuggestManualTimeZoneCapability(in.readInt())
82                 .build();
83     }
84 
85     @Override
writeToParcel(@onNull Parcel dest, int flags)86     public void writeToParcel(@NonNull Parcel dest, int flags) {
87         UserHandle.writeToParcel(mUserHandle, dest);
88         dest.writeInt(mConfigureAutoDetectionEnabledCapability);
89         dest.writeInt(mConfigureGeoDetectionEnabledCapability);
90         dest.writeInt(mSuggestManualTimeZoneCapability);
91     }
92 
93     /**
94      * Returns the capability state associated with the user's ability to modify the automatic time
95      * zone detection setting. The setting can be updated via {@link
96      * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)}.
97      */
98     @CapabilityState
getConfigureAutoDetectionEnabledCapability()99     public int getConfigureAutoDetectionEnabledCapability() {
100         return mConfigureAutoDetectionEnabledCapability;
101     }
102 
103     /**
104      * Returns the capability state associated with the user's ability to modify the geolocation
105      * detection setting. The setting can be updated via {@link
106      * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)}.
107      */
108     @CapabilityState
getConfigureGeoDetectionEnabledCapability()109     public int getConfigureGeoDetectionEnabledCapability() {
110         return mConfigureGeoDetectionEnabledCapability;
111     }
112 
113     /**
114      * Returns the capability state associated with the user's ability to manually set the time zone
115      * on a device via {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}.
116      *
117      * <p>The suggestion will be ignored in all cases unless the value is {@link
118      * Capabilities#CAPABILITY_POSSESSED}. See also
119      * {@link TimeZoneConfiguration#isAutoDetectionEnabled()}.
120      *
121      * @hide
122      */
123     @CapabilityState
getSuggestManualTimeZoneCapability()124     public int getSuggestManualTimeZoneCapability() {
125         return mSuggestManualTimeZoneCapability;
126     }
127 
128     /**
129      * Tries to create a new {@link TimeZoneConfiguration} from the {@code config} and the set of
130      * {@code requestedChanges}, if {@code this} capabilities allow. The new configuration is
131      * returned. If the capabilities do not permit one or more of the requested changes then {@code
132      * null} is returned.
133      *
134      * @hide
135      */
136     @Nullable
tryApplyConfigChanges( @onNull TimeZoneConfiguration config, @NonNull TimeZoneConfiguration requestedChanges)137     public TimeZoneConfiguration tryApplyConfigChanges(
138             @NonNull TimeZoneConfiguration config,
139             @NonNull TimeZoneConfiguration requestedChanges) {
140         TimeZoneConfiguration.Builder newConfigBuilder = new TimeZoneConfiguration.Builder(config);
141         if (requestedChanges.hasIsAutoDetectionEnabled()) {
142             if (this.getConfigureAutoDetectionEnabledCapability() < CAPABILITY_NOT_APPLICABLE) {
143                 return null;
144             }
145             newConfigBuilder.setAutoDetectionEnabled(requestedChanges.isAutoDetectionEnabled());
146         }
147 
148         if (requestedChanges.hasIsGeoDetectionEnabled()) {
149             if (this.getConfigureGeoDetectionEnabledCapability() < CAPABILITY_NOT_APPLICABLE) {
150                 return null;
151             }
152             newConfigBuilder.setGeoDetectionEnabled(requestedChanges.isGeoDetectionEnabled());
153         }
154 
155         return newConfigBuilder.build();
156     }
157 
158     @Override
describeContents()159     public int describeContents() {
160         return 0;
161     }
162 
163     @Override
equals(@ullable Object o)164     public boolean equals(@Nullable Object o) {
165         if (this == o) {
166             return true;
167         }
168         if (o == null || getClass() != o.getClass()) {
169             return false;
170         }
171         TimeZoneCapabilities that = (TimeZoneCapabilities) o;
172         return mUserHandle.equals(that.mUserHandle)
173                 && mConfigureAutoDetectionEnabledCapability
174                 == that.mConfigureAutoDetectionEnabledCapability
175                 && mConfigureGeoDetectionEnabledCapability
176                 == that.mConfigureGeoDetectionEnabledCapability
177                 && mSuggestManualTimeZoneCapability == that.mSuggestManualTimeZoneCapability;
178     }
179 
180     @Override
hashCode()181     public int hashCode() {
182         return Objects.hash(mUserHandle, mConfigureAutoDetectionEnabledCapability,
183                 mConfigureGeoDetectionEnabledCapability, mSuggestManualTimeZoneCapability);
184     }
185 
186     @Override
toString()187     public String toString() {
188         return "TimeZoneDetectorCapabilities{"
189                 + "mUserHandle=" + mUserHandle
190                 + ", mConfigureAutoDetectionEnabledCapability="
191                 + mConfigureAutoDetectionEnabledCapability
192                 + ", mConfigureGeoDetectionEnabledCapability="
193                 + mConfigureGeoDetectionEnabledCapability
194                 + ", mSuggestManualTimeZoneCapability=" + mSuggestManualTimeZoneCapability
195                 + '}';
196     }
197 
198     /** @hide */
199     public static class Builder {
200 
201         @NonNull private UserHandle mUserHandle;
202         private @CapabilityState int mConfigureAutoDetectionEnabledCapability;
203         private @CapabilityState int mConfigureGeoDetectionEnabledCapability;
204         private @CapabilityState int mSuggestManualTimeZoneCapability;
205 
Builder(@onNull UserHandle userHandle)206         public Builder(@NonNull UserHandle userHandle) {
207             mUserHandle = Objects.requireNonNull(userHandle);
208         }
209 
Builder(@onNull TimeZoneCapabilities capabilitiesToCopy)210         public Builder(@NonNull TimeZoneCapabilities capabilitiesToCopy) {
211             Objects.requireNonNull(capabilitiesToCopy);
212             mUserHandle = capabilitiesToCopy.mUserHandle;
213             mConfigureAutoDetectionEnabledCapability =
214                 capabilitiesToCopy.mConfigureAutoDetectionEnabledCapability;
215             mConfigureGeoDetectionEnabledCapability =
216                 capabilitiesToCopy.mConfigureGeoDetectionEnabledCapability;
217             mSuggestManualTimeZoneCapability =
218                 capabilitiesToCopy.mSuggestManualTimeZoneCapability;
219         }
220 
221         /** Sets the state for the automatic time zone detection enabled config. */
setConfigureAutoDetectionEnabledCapability(@apabilityState int value)222         public Builder setConfigureAutoDetectionEnabledCapability(@CapabilityState int value) {
223             this.mConfigureAutoDetectionEnabledCapability = value;
224             return this;
225         }
226 
227         /** Sets the state for the geolocation time zone detection enabled config. */
setConfigureGeoDetectionEnabledCapability(@apabilityState int value)228         public Builder setConfigureGeoDetectionEnabledCapability(@CapabilityState int value) {
229             this.mConfigureGeoDetectionEnabledCapability = value;
230             return this;
231         }
232 
233         /** Sets the state for the suggestManualTimeZone action. */
setSuggestManualTimeZoneCapability(@apabilityState int value)234         public Builder setSuggestManualTimeZoneCapability(@CapabilityState int value) {
235             this.mSuggestManualTimeZoneCapability = value;
236             return this;
237         }
238 
239         /** Returns the {@link TimeZoneCapabilities}. */
240         @NonNull
build()241         public TimeZoneCapabilities build() {
242             verifyCapabilitySet(mConfigureAutoDetectionEnabledCapability,
243                     "configureAutoDetectionEnabledCapability");
244             verifyCapabilitySet(mConfigureGeoDetectionEnabledCapability,
245                     "configureGeoDetectionEnabledCapability");
246             verifyCapabilitySet(mSuggestManualTimeZoneCapability,
247                     "suggestManualTimeZoneCapability");
248             return new TimeZoneCapabilities(this);
249         }
250 
verifyCapabilitySet(int value, String name)251         private void verifyCapabilitySet(int value, String name) {
252             if (value == 0) {
253                 throw new IllegalStateException(name + " not set");
254             }
255         }
256     }
257 }
258