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 android.annotation.NonNull; 20 import android.annotation.RequiresPermission; 21 import android.annotation.SystemApi; 22 import android.annotation.SystemService; 23 import android.app.timedetector.ITimeDetectorService; 24 import android.app.timezonedetector.ITimeZoneDetectorService; 25 import android.content.Context; 26 import android.os.RemoteException; 27 import android.os.ServiceManager; 28 import android.os.ServiceManager.ServiceNotFoundException; 29 import android.util.ArrayMap; 30 import android.util.Log; 31 32 import com.android.internal.annotations.GuardedBy; 33 34 import java.util.concurrent.Executor; 35 36 /** 37 * The interface through which system components can interact with time and time zone services. 38 * 39 * @hide 40 */ 41 @SystemApi 42 @SystemService(Context.TIME_MANAGER) 43 public final class TimeManager { 44 private static final String TAG = "time.TimeManager"; 45 private static final boolean DEBUG = false; 46 47 private final Object mLock = new Object(); 48 private final ITimeZoneDetectorService mITimeZoneDetectorService; 49 private final ITimeDetectorService mITimeDetectorService; 50 51 @GuardedBy("mLock") 52 private ITimeZoneDetectorListener mTimeZoneDetectorReceiver; 53 54 /** 55 * The registered listeners. The key is the actual listener that was registered, the value is a 56 * wrapper that ensures the listener is executed on the correct Executor. 57 */ 58 @GuardedBy("mLock") 59 private ArrayMap<TimeZoneDetectorListener, TimeZoneDetectorListener> mTimeZoneDetectorListeners; 60 61 /** @hide */ TimeManager()62 public TimeManager() throws ServiceNotFoundException { 63 // TimeManager is an API over one or possibly more services. At least until there's an 64 // internal refactoring. 65 mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface( 66 ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE)); 67 mITimeDetectorService = ITimeDetectorService.Stub.asInterface( 68 ServiceManager.getServiceOrThrow(Context.TIME_DETECTOR_SERVICE)); 69 } 70 71 /** 72 * Returns the calling user's time zone capabilities and configuration. 73 */ 74 @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) 75 @NonNull getTimeZoneCapabilitiesAndConfig()76 public TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig() { 77 if (DEBUG) { 78 Log.d(TAG, "getTimeZoneCapabilitiesAndConfig called"); 79 } 80 try { 81 return mITimeZoneDetectorService.getCapabilitiesAndConfig(); 82 } catch (RemoteException e) { 83 throw e.rethrowFromSystemServer(); 84 } 85 } 86 87 /** 88 * Returns the calling user's time capabilities and configuration. 89 * 90 * @hide 91 */ 92 @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) 93 @NonNull getTimeCapabilitiesAndConfig()94 public TimeCapabilitiesAndConfig getTimeCapabilitiesAndConfig() { 95 if (DEBUG) { 96 Log.d(TAG, "getTimeCapabilitiesAndConfig called"); 97 } 98 try { 99 return mITimeDetectorService.getCapabilitiesAndConfig(); 100 } catch (RemoteException e) { 101 throw e.rethrowFromSystemServer(); 102 } 103 } 104 105 /** 106 * Modifies the time detection configuration. 107 * 108 * @return {@code true} if all the configuration settings specified have been set to the 109 * new values, {@code false} if none have 110 * 111 * @hide 112 */ 113 @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) updateTimeConfiguration(@onNull TimeConfiguration configuration)114 public boolean updateTimeConfiguration(@NonNull TimeConfiguration configuration) { 115 if (DEBUG) { 116 Log.d(TAG, "updateTimeConfiguration called: " + configuration); 117 } 118 try { 119 return mITimeDetectorService.updateConfiguration(configuration); 120 } catch (RemoteException e) { 121 throw e.rethrowFromSystemServer(); 122 } 123 } 124 125 /** 126 * Modifies the time zone detection configuration. 127 * 128 * <p>Configuration settings vary in scope: some may be global (affect all users), others may be 129 * specific to the current user. 130 * 131 * <p>The ability to modify configuration settings can be subject to restrictions. For 132 * example, they may be determined by device hardware, general policy (i.e. only the primary 133 * user can set them), or by a managed device policy. Use {@link 134 * #getTimeZoneCapabilitiesAndConfig()} to obtain information at runtime about the user's 135 * capabilities. 136 * 137 * <p>Attempts to modify configuration settings with capabilities that are {@link 138 * Capabilities#CAPABILITY_NOT_SUPPORTED} or {@link 139 * Capabilities#CAPABILITY_NOT_ALLOWED} will have no effect and a {@code false} 140 * will be returned. Modifying configuration settings with capabilities that are {@link 141 * Capabilities#CAPABILITY_NOT_APPLICABLE} or {@link 142 * Capabilities#CAPABILITY_POSSESSED} will succeed. See {@link 143 * TimeZoneCapabilities} for further details. 144 * 145 * <p>If the supplied configuration only has some values set, then only the specified settings 146 * will be updated (where the user's capabilities allow) and other settings will be left 147 * unchanged. 148 * 149 * @return {@code true} if all the configuration settings specified have been set to the 150 * new values, {@code false} if none have 151 */ 152 @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) updateTimeZoneConfiguration(@onNull TimeZoneConfiguration configuration)153 public boolean updateTimeZoneConfiguration(@NonNull TimeZoneConfiguration configuration) { 154 if (DEBUG) { 155 Log.d(TAG, "updateTimeZoneConfiguration called: " + configuration); 156 } 157 try { 158 return mITimeZoneDetectorService.updateConfiguration(configuration); 159 } catch (RemoteException e) { 160 throw e.rethrowFromSystemServer(); 161 } 162 } 163 164 /** 165 * An interface that can be used to listen for changes to the time zone detector behavior. 166 */ 167 @FunctionalInterface 168 public interface TimeZoneDetectorListener { 169 /** 170 * Called when something about the time zone detector behavior on the device has changed. 171 * For example, this could be because the current user has switched, one of the global or 172 * user's settings been changed, or something that could affect a user's capabilities with 173 * respect to the time zone detector has changed. Because different users can have different 174 * configuration and capabilities, this method may be called when nothing has changed for 175 * the receiving user. 176 */ onChange()177 void onChange(); 178 } 179 180 /** 181 * Registers a listener that will be informed when something about the time zone detector 182 * behavior changes. 183 */ 184 @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) addTimeZoneDetectorListener(@onNull Executor executor, @NonNull TimeZoneDetectorListener listener)185 public void addTimeZoneDetectorListener(@NonNull Executor executor, 186 @NonNull TimeZoneDetectorListener listener) { 187 188 if (DEBUG) { 189 Log.d(TAG, "addTimeZoneDetectorListener called: " + listener); 190 } 191 synchronized (mLock) { 192 if (mTimeZoneDetectorListeners == null) { 193 mTimeZoneDetectorListeners = new ArrayMap<>(); 194 } else if (mTimeZoneDetectorListeners.containsKey(listener)) { 195 return; 196 } 197 198 if (mTimeZoneDetectorReceiver == null) { 199 ITimeZoneDetectorListener iListener = new ITimeZoneDetectorListener.Stub() { 200 @Override 201 public void onChange() { 202 notifyTimeZoneDetectorListeners(); 203 } 204 }; 205 mTimeZoneDetectorReceiver = iListener; 206 try { 207 mITimeZoneDetectorService.addListener(mTimeZoneDetectorReceiver); 208 } catch (RemoteException e) { 209 throw e.rethrowFromSystemServer(); 210 } 211 } 212 mTimeZoneDetectorListeners.put(listener, () -> executor.execute(listener::onChange)); 213 } 214 } 215 notifyTimeZoneDetectorListeners()216 private void notifyTimeZoneDetectorListeners() { 217 ArrayMap<TimeZoneDetectorListener, TimeZoneDetectorListener> timeZoneDetectorListeners; 218 synchronized (mLock) { 219 if (mTimeZoneDetectorListeners == null || mTimeZoneDetectorListeners.isEmpty()) { 220 return; 221 } 222 timeZoneDetectorListeners = new ArrayMap<>(mTimeZoneDetectorListeners); 223 } 224 int size = timeZoneDetectorListeners.size(); 225 for (int i = 0; i < size; i++) { 226 timeZoneDetectorListeners.valueAt(i).onChange(); 227 } 228 } 229 230 /** 231 * Removes a listener previously passed to 232 * {@link #addTimeZoneDetectorListener(Executor, TimeZoneDetectorListener)} 233 */ 234 @RequiresPermission(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION) removeTimeZoneDetectorListener(@onNull TimeZoneDetectorListener listener)235 public void removeTimeZoneDetectorListener(@NonNull TimeZoneDetectorListener listener) { 236 if (DEBUG) { 237 Log.d(TAG, "removeConfigurationListener called: " + listener); 238 } 239 240 synchronized (mLock) { 241 if (mTimeZoneDetectorListeners == null || mTimeZoneDetectorListeners.isEmpty()) { 242 return; 243 } 244 mTimeZoneDetectorListeners.remove(listener); 245 246 // If the last local listener has been removed, remove and discard the 247 // mTimeZoneDetectorReceiver. 248 if (mTimeZoneDetectorListeners.isEmpty()) { 249 try { 250 mITimeZoneDetectorService.removeListener(mTimeZoneDetectorReceiver); 251 } catch (RemoteException e) { 252 throw e.rethrowFromSystemServer(); 253 } finally { 254 mTimeZoneDetectorReceiver = null; 255 } 256 } 257 } 258 } 259 260 /** 261 * Suggests the current time from an external time source. For example, a form factor-specific 262 * HAL. This time <em>may</em> be used to set the device system clock, depending on the device 263 * configuration and user settings. This method call is processed asynchronously. 264 * See {@link ExternalTimeSuggestion} for more details. 265 */ 266 @RequiresPermission(android.Manifest.permission.SUGGEST_EXTERNAL_TIME) suggestExternalTime(@onNull ExternalTimeSuggestion timeSuggestion)267 public void suggestExternalTime(@NonNull ExternalTimeSuggestion timeSuggestion) { 268 if (DEBUG) { 269 Log.d(TAG, "suggestExternalTime called: " + timeSuggestion); 270 } 271 try { 272 mITimeDetectorService.suggestExternalTime(timeSuggestion); 273 } catch (RemoteException e) { 274 throw e.rethrowFromSystemServer(); 275 } 276 } 277 } 278