1 /*
2  * Copyright (C) 2021 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 org.junit.Assert.assertNotNull;
20 
21 import android.annotation.NonNull;
22 import android.annotation.UserIdInt;
23 import android.app.time.TimeZoneCapabilities;
24 import android.app.time.TimeZoneConfiguration;
25 
26 import java.time.Duration;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Optional;
30 
31 /**
32  * A partially implemented, fake implementation of ServiceConfigAccessor for tests.
33  *
34  * <p>This class has rudamentary support for multiple users, but unlike the real thing, it doesn't
35  * simulate that some settings are global and shared between users. It also delivers config updates
36  * synchronously.
37  */
38 public class FakeServiceConfigAccessor implements ServiceConfigAccessor {
39 
40     private final List<StateChangeListener> mConfigurationInternalChangeListeners =
41             new ArrayList<>();
42     private ConfigurationInternal mCurrentUserConfigurationInternal;
43     private ConfigurationInternal mOtherUserConfigurationInternal;
44 
45     @Override
addConfigurationInternalChangeListener(StateChangeListener listener)46     public void addConfigurationInternalChangeListener(StateChangeListener listener) {
47         mConfigurationInternalChangeListeners.add(listener);
48     }
49 
50     @Override
removeConfigurationInternalChangeListener(StateChangeListener listener)51     public void removeConfigurationInternalChangeListener(StateChangeListener listener) {
52         mConfigurationInternalChangeListeners.remove(listener);
53     }
54 
55     @Override
getCurrentUserConfigurationInternal()56     public ConfigurationInternal getCurrentUserConfigurationInternal() {
57         return getConfigurationInternal(mCurrentUserConfigurationInternal.getUserId());
58     }
59 
60     @Override
updateConfiguration( @serIdInt int userId, @NonNull TimeZoneConfiguration requestedChanges, boolean bypassUserPolicyChecks)61     public boolean updateConfiguration(
62             @UserIdInt int userId, @NonNull TimeZoneConfiguration requestedChanges,
63             boolean bypassUserPolicyChecks) {
64         assertNotNull(mCurrentUserConfigurationInternal);
65         assertNotNull(requestedChanges);
66 
67         ConfigurationInternal toUpdate = getConfigurationInternal(userId);
68 
69         // Simulate the real strategy's behavior: the new configuration will be updated to be the
70         // old configuration merged with the new if the user has the capability to update the
71         // settings. Then, if the configuration changed, the change listener is invoked.
72         TimeZoneCapabilities capabilities = toUpdate.asCapabilities(bypassUserPolicyChecks);
73         TimeZoneConfiguration configuration = toUpdate.asConfiguration();
74         TimeZoneConfiguration newConfiguration =
75                 capabilities.tryApplyConfigChanges(configuration, requestedChanges);
76         if (newConfiguration == null) {
77             return false;
78         }
79 
80         if (!newConfiguration.equals(configuration)) {
81             ConfigurationInternal updatedConfiguration = toUpdate.merge(newConfiguration);
82             if (updatedConfiguration.getUserId() == mCurrentUserConfigurationInternal.getUserId()) {
83                 mCurrentUserConfigurationInternal = updatedConfiguration;
84             } else if (mOtherUserConfigurationInternal != null
85                     && updatedConfiguration.getUserId()
86                     == mOtherUserConfigurationInternal.getUserId()) {
87                 mOtherUserConfigurationInternal = updatedConfiguration;
88             }
89             // Note: Unlike the real strategy, the listeners are invoked synchronously.
90             notifyConfigurationChange();
91         }
92         return true;
93     }
94 
initializeCurrentUserConfiguration(ConfigurationInternal configurationInternal)95     void initializeCurrentUserConfiguration(ConfigurationInternal configurationInternal) {
96         mCurrentUserConfigurationInternal = configurationInternal;
97     }
98 
initializeOtherUserConfiguration(ConfigurationInternal configurationInternal)99     void initializeOtherUserConfiguration(ConfigurationInternal configurationInternal) {
100         mOtherUserConfigurationInternal = configurationInternal;
101     }
102 
simulateCurrentUserConfigurationInternalChange( ConfigurationInternal configurationInternal)103     void simulateCurrentUserConfigurationInternalChange(
104             ConfigurationInternal configurationInternal) {
105         mCurrentUserConfigurationInternal = configurationInternal;
106         // Note: Unlike the real strategy, the listeners are invoked synchronously.
107         notifyConfigurationChange();
108     }
109 
simulateOtherUserConfigurationInternalChange(ConfigurationInternal configurationInternal)110     void simulateOtherUserConfigurationInternalChange(ConfigurationInternal configurationInternal) {
111         mOtherUserConfigurationInternal = configurationInternal;
112         // Note: Unlike the real strategy, the listeners are invoked synchronously.
113         notifyConfigurationChange();
114     }
115 
116     @Override
getConfigurationInternal(int userId)117     public ConfigurationInternal getConfigurationInternal(int userId) {
118         if (userId == mCurrentUserConfigurationInternal.getUserId()) {
119             return mCurrentUserConfigurationInternal;
120         } else if (mOtherUserConfigurationInternal != null
121                     && userId == mOtherUserConfigurationInternal.getUserId()) {
122             return mOtherUserConfigurationInternal;
123         }
124         throw new AssertionError("userId not known: " + userId);
125     }
126 
127     @Override
addLocationTimeZoneManagerConfigListener(StateChangeListener listener)128     public void addLocationTimeZoneManagerConfigListener(StateChangeListener listener) {
129         failUnimplemented();
130     }
131 
132     @Override
isTelephonyTimeZoneDetectionFeatureSupported()133     public boolean isTelephonyTimeZoneDetectionFeatureSupported() {
134         return failUnimplemented();
135     }
136 
137     @Override
isGeoTimeZoneDetectionFeatureSupportedInConfig()138     public boolean isGeoTimeZoneDetectionFeatureSupportedInConfig() {
139         return failUnimplemented();
140     }
141 
142     @Override
isGeoTimeZoneDetectionFeatureSupported()143     public boolean isGeoTimeZoneDetectionFeatureSupported() {
144         return failUnimplemented();
145     }
146 
147     @Override
getPrimaryLocationTimeZoneProviderPackageName()148     public String getPrimaryLocationTimeZoneProviderPackageName() {
149         return failUnimplemented();
150     }
151 
152     @Override
setTestPrimaryLocationTimeZoneProviderPackageName( String testPrimaryLocationTimeZoneProviderPackageName)153     public void setTestPrimaryLocationTimeZoneProviderPackageName(
154             String testPrimaryLocationTimeZoneProviderPackageName) {
155         failUnimplemented();
156     }
157 
158     @Override
isTestPrimaryLocationTimeZoneProvider()159     public boolean isTestPrimaryLocationTimeZoneProvider() {
160         return failUnimplemented();
161     }
162 
163     @Override
getSecondaryLocationTimeZoneProviderPackageName()164     public String getSecondaryLocationTimeZoneProviderPackageName() {
165         return failUnimplemented();
166     }
167 
168     @Override
setTestSecondaryLocationTimeZoneProviderPackageName( String testSecondaryLocationTimeZoneProviderPackageName)169     public void setTestSecondaryLocationTimeZoneProviderPackageName(
170             String testSecondaryLocationTimeZoneProviderPackageName) {
171         failUnimplemented();
172     }
173 
174     @Override
isTestSecondaryLocationTimeZoneProvider()175     public boolean isTestSecondaryLocationTimeZoneProvider() {
176         return failUnimplemented();
177     }
178 
179     @Override
setRecordStateChangesForTests(boolean enabled)180     public void setRecordStateChangesForTests(boolean enabled) {
181         failUnimplemented();
182     }
183 
184     @Override
getRecordStateChangesForTests()185     public boolean getRecordStateChangesForTests() {
186         return failUnimplemented();
187     }
188 
189     @Override
getPrimaryLocationTimeZoneProviderMode()190     public @ProviderMode String getPrimaryLocationTimeZoneProviderMode() {
191         return failUnimplemented();
192     }
193 
194     @Override
getSecondaryLocationTimeZoneProviderMode()195     public @ProviderMode String getSecondaryLocationTimeZoneProviderMode() {
196         return failUnimplemented();
197     }
198 
199     @Override
isGeoDetectionEnabledForUsersByDefault()200     public boolean isGeoDetectionEnabledForUsersByDefault() {
201         return failUnimplemented();
202     }
203 
204     @Override
getGeoDetectionSettingEnabledOverride()205     public Optional<Boolean> getGeoDetectionSettingEnabledOverride() {
206         return failUnimplemented();
207     }
208 
209     @Override
getLocationTimeZoneProviderInitializationTimeout()210     public Duration getLocationTimeZoneProviderInitializationTimeout() {
211         return failUnimplemented();
212     }
213 
214     @Override
getLocationTimeZoneProviderInitializationTimeoutFuzz()215     public Duration getLocationTimeZoneProviderInitializationTimeoutFuzz() {
216         return failUnimplemented();
217     }
218 
219     @Override
getLocationTimeZoneUncertaintyDelay()220     public Duration getLocationTimeZoneUncertaintyDelay() {
221         return failUnimplemented();
222     }
223 
224     @Override
getLocationTimeZoneProviderEventFilteringAgeThreshold()225     public Duration getLocationTimeZoneProviderEventFilteringAgeThreshold() {
226         return failUnimplemented();
227     }
228 
229     @Override
resetVolatileTestConfig()230     public void resetVolatileTestConfig() {
231         failUnimplemented();
232     }
233 
notifyConfigurationChange()234     private void notifyConfigurationChange() {
235         for (StateChangeListener listener : mConfigurationInternalChangeListeners) {
236             listener.onChange();
237         }
238     }
239 
240     @SuppressWarnings("UnusedReturnValue")
failUnimplemented()241     private static <T> T failUnimplemented() {
242         throw new AssertionError("Unimplemented");
243     }
244 }
245