1 /* 2 * Copyright (C) 2018 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.timedetector; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.UserIdInt; 22 import android.app.time.ExternalTimeSuggestion; 23 import android.app.time.ITimeDetectorListener; 24 import android.app.time.TimeCapabilitiesAndConfig; 25 import android.app.time.TimeConfiguration; 26 import android.app.time.TimeState; 27 import android.app.time.UnixEpochTime; 28 import android.app.timedetector.ITimeDetectorService; 29 import android.app.timedetector.ManualTimeSuggestion; 30 import android.app.timedetector.TelephonyTimeSuggestion; 31 import android.content.Context; 32 import android.os.Handler; 33 import android.os.IBinder; 34 import android.os.ParcelableException; 35 import android.os.RemoteException; 36 import android.os.ResultReceiver; 37 import android.os.ShellCallback; 38 import android.os.SystemClock; 39 import android.util.ArrayMap; 40 import android.util.IndentingPrintWriter; 41 import android.util.NtpTrustedTime; 42 import android.util.Slog; 43 44 import com.android.internal.annotations.GuardedBy; 45 import com.android.internal.annotations.VisibleForTesting; 46 import com.android.internal.util.DumpUtils; 47 import com.android.server.FgThread; 48 import com.android.server.SystemService; 49 import com.android.server.location.gnss.TimeDetectorNetworkTimeHelper; 50 import com.android.server.timezonedetector.CallerIdentityInjector; 51 import com.android.server.timezonedetector.CurrentUserIdentityInjector; 52 53 import java.io.FileDescriptor; 54 import java.io.PrintWriter; 55 import java.net.InetSocketAddress; 56 import java.time.DateTimeException; 57 import java.util.Objects; 58 59 /** 60 * The implementation of ITimeDetectorService.aidl. 61 * 62 * <p>This service is implemented as a wrapper around {@link TimeDetectorStrategy}. It handles 63 * interaction with Android framework classes, enforcing caller permissions, capturing user identity 64 * and making calls async, leaving the (consequently more testable) {@link TimeDetectorStrategy} 65 * implementation to deal with the logic around time detection. 66 */ 67 public final class TimeDetectorService extends ITimeDetectorService.Stub 68 implements IBinder.DeathRecipient { 69 static final String TAG = "time_detector"; 70 71 public static class Lifecycle extends SystemService { 72 Lifecycle(@onNull Context context)73 public Lifecycle(@NonNull Context context) { 74 super(context); 75 } 76 77 @Override onStart()78 public void onStart() { 79 Context context = getContext(); 80 Handler handler = FgThread.getHandler(); 81 82 ServiceConfigAccessor serviceConfigAccessor = 83 ServiceConfigAccessorImpl.getInstance(context); 84 TimeDetectorStrategy timeDetectorStrategy = 85 TimeDetectorStrategyImpl.create(context, handler, serviceConfigAccessor); 86 87 // Create and publish the local service for use by internal callers. 88 CurrentUserIdentityInjector currentUserIdentityInjector = 89 CurrentUserIdentityInjector.REAL; 90 TimeDetectorInternal internal = new TimeDetectorInternalImpl( 91 context, handler, currentUserIdentityInjector, serviceConfigAccessor, 92 timeDetectorStrategy); 93 publishLocalService(TimeDetectorInternal.class, internal); 94 95 CallerIdentityInjector callerIdentityInjector = CallerIdentityInjector.REAL; 96 TimeDetectorService service = new TimeDetectorService( 97 context, handler, callerIdentityInjector, timeDetectorStrategy, 98 NtpTrustedTime.getInstance(context)); 99 100 // Publish the binder service so it can be accessed from other (appropriately 101 // permissioned) processes. 102 publishBinderService(Context.TIME_DETECTOR_SERVICE, service); 103 } 104 } 105 106 @NonNull private final Handler mHandler; 107 @NonNull private final Context mContext; 108 @NonNull private final CallerIdentityInjector mCallerIdentityInjector; 109 @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy; 110 @NonNull private final NtpTrustedTime mNtpTrustedTime; 111 112 /** 113 * Holds the listeners. The key is the {@link IBinder} associated with the listener, the value 114 * is the listener itself. 115 */ 116 @GuardedBy("mListeners") 117 @NonNull 118 private final ArrayMap<IBinder, ITimeDetectorListener> mListeners = new ArrayMap<>(); 119 120 @VisibleForTesting TimeDetectorService(@onNull Context context, @NonNull Handler handler, @NonNull CallerIdentityInjector callerIdentityInjector, @NonNull TimeDetectorStrategy timeDetectorStrategy, @NonNull NtpTrustedTime ntpTrustedTime)121 public TimeDetectorService(@NonNull Context context, @NonNull Handler handler, 122 @NonNull CallerIdentityInjector callerIdentityInjector, 123 @NonNull TimeDetectorStrategy timeDetectorStrategy, 124 @NonNull NtpTrustedTime ntpTrustedTime) { 125 mContext = Objects.requireNonNull(context); 126 mHandler = Objects.requireNonNull(handler); 127 mCallerIdentityInjector = Objects.requireNonNull(callerIdentityInjector); 128 mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy); 129 mNtpTrustedTime = Objects.requireNonNull(ntpTrustedTime); 130 131 // Wire up a change listener so that ITimeDetectorListeners can be notified when the 132 // detector state changes for any reason. 133 mTimeDetectorStrategy.addChangeListener( 134 () -> mHandler.post(this::handleChangeOnHandlerThread)); 135 } 136 137 @Override 138 @NonNull getCapabilitiesAndConfig()139 public TimeCapabilitiesAndConfig getCapabilitiesAndConfig() { 140 int userId = mCallerIdentityInjector.getCallingUserId(); 141 return getTimeCapabilitiesAndConfig(userId); 142 } 143 getTimeCapabilitiesAndConfig(@serIdInt int userId)144 private TimeCapabilitiesAndConfig getTimeCapabilitiesAndConfig(@UserIdInt int userId) { 145 enforceManageTimeDetectorPermission(); 146 147 final long token = mCallerIdentityInjector.clearCallingIdentity(); 148 try { 149 final boolean bypassUserPolicyChecks = false; 150 return mTimeDetectorStrategy.getCapabilitiesAndConfig(userId, bypassUserPolicyChecks); 151 } finally { 152 mCallerIdentityInjector.restoreCallingIdentity(token); 153 } 154 } 155 156 @Override updateConfiguration(@onNull TimeConfiguration configuration)157 public boolean updateConfiguration(@NonNull TimeConfiguration configuration) { 158 int callingUserId = mCallerIdentityInjector.getCallingUserId(); 159 return updateConfiguration(callingUserId, configuration); 160 } 161 162 /** 163 * Updates the user's configuration. Exposed for use by {@link TimeDetectorShellCommand}. 164 */ updateConfiguration(@serIdInt int userId, @NonNull TimeConfiguration configuration)165 boolean updateConfiguration(@UserIdInt int userId, @NonNull TimeConfiguration configuration) { 166 // Resolve constants like USER_CURRENT to the true user ID as needed. 167 int resolvedUserId = mCallerIdentityInjector.resolveUserId(userId, "updateConfiguration"); 168 169 enforceManageTimeDetectorPermission(); 170 171 Objects.requireNonNull(configuration); 172 173 final long token = mCallerIdentityInjector.clearCallingIdentity(); 174 try { 175 final boolean bypassUserPolicyChecks = false; 176 return mTimeDetectorStrategy.updateConfiguration( 177 resolvedUserId, configuration, bypassUserPolicyChecks); 178 } finally { 179 mCallerIdentityInjector.restoreCallingIdentity(token); 180 } 181 } 182 183 @Override addListener(@onNull ITimeDetectorListener listener)184 public void addListener(@NonNull ITimeDetectorListener listener) { 185 enforceManageTimeDetectorPermission(); 186 Objects.requireNonNull(listener); 187 188 synchronized (mListeners) { 189 IBinder listenerBinder = listener.asBinder(); 190 if (mListeners.containsKey(listenerBinder)) { 191 return; 192 } 193 try { 194 // Ensure the reference to the listener will be removed if the client process dies. 195 listenerBinder.linkToDeath(this, 0 /* flags */); 196 197 // Only add the listener if we can linkToDeath(). 198 mListeners.put(listenerBinder, listener); 199 } catch (RemoteException e) { 200 Slog.e(TAG, "Unable to linkToDeath() for listener=" + listener, e); 201 } 202 } 203 } 204 205 @Override removeListener(@onNull ITimeDetectorListener listener)206 public void removeListener(@NonNull ITimeDetectorListener listener) { 207 enforceManageTimeDetectorPermission(); 208 Objects.requireNonNull(listener); 209 210 synchronized (mListeners) { 211 IBinder listenerBinder = listener.asBinder(); 212 boolean removedListener = false; 213 if (mListeners.remove(listenerBinder) != null) { 214 // Stop listening for the client process to die. 215 listenerBinder.unlinkToDeath(this, 0 /* flags */); 216 removedListener = true; 217 } 218 if (!removedListener) { 219 Slog.w(TAG, "Client asked to remove listener=" + listener 220 + ", but no listeners were removed." 221 + " mListeners=" + mListeners); 222 } 223 } 224 } 225 226 @Override binderDied()227 public void binderDied() { 228 // Should not be used as binderDied(IBinder who) is overridden. 229 Slog.wtf(TAG, "binderDied() called unexpectedly."); 230 } 231 232 /** 233 * Called when one of the ITimeDetectorListener processes dies before calling 234 * {@link #removeListener(ITimeDetectorListener)}. 235 */ 236 @Override binderDied(IBinder who)237 public void binderDied(IBinder who) { 238 synchronized (mListeners) { 239 boolean removedListener = false; 240 final int listenerCount = mListeners.size(); 241 for (int listenerIndex = listenerCount - 1; listenerIndex >= 0; listenerIndex--) { 242 IBinder listenerBinder = mListeners.keyAt(listenerIndex); 243 if (listenerBinder.equals(who)) { 244 mListeners.removeAt(listenerIndex); 245 removedListener = true; 246 break; 247 } 248 } 249 if (!removedListener) { 250 Slog.w(TAG, "Notified of binder death for who=" + who 251 + ", but did not remove any listeners." 252 + " mListeners=" + mListeners); 253 } 254 } 255 } 256 handleChangeOnHandlerThread()257 private void handleChangeOnHandlerThread() { 258 // Configuration has changed, but each user may have a different view of the configuration. 259 // It's possible that this will cause unnecessary notifications but that shouldn't be a 260 // problem. 261 synchronized (mListeners) { 262 final int listenerCount = mListeners.size(); 263 for (int listenerIndex = 0; listenerIndex < listenerCount; listenerIndex++) { 264 ITimeDetectorListener listener = mListeners.valueAt(listenerIndex); 265 try { 266 // No need to surrender the mListeners lock while doing this: 267 // ITimeDetectorListener is declared "oneway". 268 listener.onChange(); 269 } catch (RemoteException e) { 270 Slog.w(TAG, "Unable to notify listener=" + listener, e); 271 } 272 } 273 } 274 } 275 276 @Override getTimeState()277 public TimeState getTimeState() { 278 enforceManageTimeDetectorPermission(); 279 280 final long token = mCallerIdentityInjector.clearCallingIdentity(); 281 try { 282 return mTimeDetectorStrategy.getTimeState(); 283 } finally { 284 mCallerIdentityInjector.restoreCallingIdentity(token); 285 } 286 } 287 288 /** 289 * Sets the system time state. See {@link TimeState} for details. For use by {@link 290 * TimeDetectorShellCommand}. 291 */ setTimeState(@onNull TimeState timeState)292 void setTimeState(@NonNull TimeState timeState) { 293 enforceManageTimeDetectorPermission(); 294 295 final long token = mCallerIdentityInjector.clearCallingIdentity(); 296 try { 297 mTimeDetectorStrategy.setTimeState(timeState); 298 } finally { 299 mCallerIdentityInjector.restoreCallingIdentity(token); 300 } 301 } 302 303 @Override confirmTime(@onNull UnixEpochTime time)304 public boolean confirmTime(@NonNull UnixEpochTime time) { 305 enforceManageTimeDetectorPermission(); 306 Objects.requireNonNull(time); 307 308 final long token = mCallerIdentityInjector.clearCallingIdentity(); 309 try { 310 return mTimeDetectorStrategy.confirmTime(time); 311 } finally { 312 mCallerIdentityInjector.restoreCallingIdentity(token); 313 } 314 } 315 316 @Override setManualTime(@onNull ManualTimeSuggestion suggestion)317 public boolean setManualTime(@NonNull ManualTimeSuggestion suggestion) { 318 enforceManageTimeDetectorPermission(); 319 Objects.requireNonNull(suggestion); 320 321 // This calls suggestManualTime() as the logic is identical, it only differs in the 322 // permission required, which is handled on the line above. 323 int userId = mCallerIdentityInjector.getCallingUserId(); 324 final long token = mCallerIdentityInjector.clearCallingIdentity(); 325 try { 326 final boolean bypassUserPolicyChecks = false; 327 return mTimeDetectorStrategy.suggestManualTime( 328 userId, suggestion, bypassUserPolicyChecks); 329 } finally { 330 mCallerIdentityInjector.restoreCallingIdentity(token); 331 } 332 } 333 334 @Override suggestTelephonyTime(@onNull TelephonyTimeSuggestion timeSignal)335 public void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSignal) { 336 enforceSuggestTelephonyTimePermission(); 337 Objects.requireNonNull(timeSignal); 338 339 mHandler.post(() -> mTimeDetectorStrategy.suggestTelephonyTime(timeSignal)); 340 } 341 342 @Override suggestManualTime(@onNull ManualTimeSuggestion timeSignal)343 public boolean suggestManualTime(@NonNull ManualTimeSuggestion timeSignal) { 344 enforceSuggestManualTimePermission(); 345 Objects.requireNonNull(timeSignal); 346 347 int userId = mCallerIdentityInjector.getCallingUserId(); 348 final long token = mCallerIdentityInjector.clearCallingIdentity(); 349 try { 350 final boolean bypassUserPolicyChecks = false; 351 return mTimeDetectorStrategy.suggestManualTime( 352 userId, timeSignal, bypassUserPolicyChecks); 353 } finally { 354 mCallerIdentityInjector.restoreCallingIdentity(token); 355 } 356 } 357 358 /** 359 * Suggests network time with permission checks. For use by {@link TimeDetectorShellCommand}. 360 */ suggestNetworkTime(@onNull NetworkTimeSuggestion suggestion)361 void suggestNetworkTime(@NonNull NetworkTimeSuggestion suggestion) { 362 enforceSuggestNetworkTimePermission(); 363 Objects.requireNonNull(suggestion); 364 365 mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(suggestion)); 366 } 367 368 /** 369 * Clears the cached network time information. For use during tests to simulate when no network 370 * time has been made available. For use by {@link TimeDetectorShellCommand}. 371 * 372 * <p>This operation takes place in the calling thread. 373 */ clearLatestNetworkTime()374 void clearLatestNetworkTime() { 375 enforceSuggestNetworkTimePermission(); 376 377 final long token = mCallerIdentityInjector.clearCallingIdentity(); 378 try { 379 mTimeDetectorStrategy.clearLatestNetworkSuggestion(); 380 } finally { 381 mCallerIdentityInjector.restoreCallingIdentity(token); 382 } 383 } 384 385 @Override latestNetworkTime()386 public UnixEpochTime latestNetworkTime() { 387 NetworkTimeSuggestion latestNetworkTime; 388 // TODO(b/222295093): Remove this condition once we can be sure that all uses of 389 // NtpTrustedTime result in a suggestion being made to the time detector. 390 // mNtpTrustedTime can be removed once this happens. 391 if (TimeDetectorNetworkTimeHelper.isInUse()) { 392 // The new implementation. 393 latestNetworkTime = mTimeDetectorStrategy.getLatestNetworkSuggestion(); 394 } else { 395 // The old implementation. 396 NtpTrustedTime.TimeResult ntpResult = mNtpTrustedTime.getCachedTimeResult(); 397 if (ntpResult != null) { 398 latestNetworkTime = new NetworkTimeSuggestion( 399 new UnixEpochTime( 400 ntpResult.getElapsedRealtimeMillis(), ntpResult.getTimeMillis()), 401 ntpResult.getUncertaintyMillis()); 402 } else { 403 latestNetworkTime = null; 404 } 405 } 406 if (latestNetworkTime == null) { 407 throw new ParcelableException(new DateTimeException("Missing network time fix")); 408 } 409 return latestNetworkTime.getUnixEpochTime(); 410 } 411 412 /** 413 * Returns the latest network suggestion accepted. For use by {@link TimeDetectorShellCommand}. 414 */ 415 @Nullable getLatestNetworkSuggestion()416 NetworkTimeSuggestion getLatestNetworkSuggestion() { 417 return mTimeDetectorStrategy.getLatestNetworkSuggestion(); 418 } 419 420 /** 421 * Suggests GNSS time with permission checks. For use by {@link TimeDetectorShellCommand}. 422 */ suggestGnssTime(@onNull GnssTimeSuggestion timeSignal)423 void suggestGnssTime(@NonNull GnssTimeSuggestion timeSignal) { 424 enforceSuggestGnssTimePermission(); 425 Objects.requireNonNull(timeSignal); 426 427 mHandler.post(() -> mTimeDetectorStrategy.suggestGnssTime(timeSignal)); 428 } 429 430 @Override suggestExternalTime(@onNull ExternalTimeSuggestion timeSignal)431 public void suggestExternalTime(@NonNull ExternalTimeSuggestion timeSignal) { 432 enforceSuggestExternalTimePermission(); 433 Objects.requireNonNull(timeSignal); 434 435 mHandler.post(() -> mTimeDetectorStrategy.suggestExternalTime(timeSignal)); 436 } 437 438 /** 439 * Sets the network time for testing {@link SystemClock#currentNetworkTimeClock()}. 440 * 441 * <p>This operation takes place in the calling thread. 442 */ setNetworkTimeForSystemClockForTests( @onNull UnixEpochTime unixEpochTime, int uncertaintyMillis)443 void setNetworkTimeForSystemClockForTests( 444 @NonNull UnixEpochTime unixEpochTime, int uncertaintyMillis) { 445 enforceSuggestNetworkTimePermission(); 446 447 // TODO(b/222295093): Remove this condition once we can be sure that all uses of 448 // NtpTrustedTime result in a suggestion being made to the time detector. 449 // mNtpTrustedTime can be removed once this happens. 450 if (TimeDetectorNetworkTimeHelper.isInUse()) { 451 NetworkTimeSuggestion suggestion = 452 new NetworkTimeSuggestion(unixEpochTime, uncertaintyMillis); 453 suggestion.addDebugInfo("Injected for tests"); 454 mTimeDetectorStrategy.suggestNetworkTime(suggestion); 455 } else { 456 NtpTrustedTime.TimeResult timeResult = new NtpTrustedTime.TimeResult( 457 unixEpochTime.getUnixEpochTimeMillis(), 458 unixEpochTime.getElapsedRealtimeMillis(), 459 uncertaintyMillis, 460 InetSocketAddress.createUnresolved("time.set.for.tests", 123)); 461 mNtpTrustedTime.setCachedTimeResult(timeResult); 462 } 463 } 464 465 /** 466 * Clears the network time for testing {@link SystemClock#currentNetworkTimeClock()}. 467 * 468 * <p>This operation takes place in the calling thread. 469 */ clearNetworkTimeForSystemClockForTests()470 void clearNetworkTimeForSystemClockForTests() { 471 enforceSuggestNetworkTimePermission(); 472 473 final long token = mCallerIdentityInjector.clearCallingIdentity(); 474 try { 475 // TODO(b/222295093): Remove this condition once we can be sure that all uses of 476 // NtpTrustedTime result in a suggestion being made to the time detector. 477 // mNtpTrustedTime can be removed once this happens. 478 if (TimeDetectorNetworkTimeHelper.isInUse()) { 479 // Clear the latest network suggestion. Done in all c 480 mTimeDetectorStrategy.clearLatestNetworkSuggestion(); 481 } else { 482 mNtpTrustedTime.clearCachedTimeResult(); 483 } 484 } finally { 485 mCallerIdentityInjector.restoreCallingIdentity(token); 486 } 487 } 488 489 @Override dump(@onNull FileDescriptor fd, @NonNull PrintWriter pw, @Nullable String[] args)490 protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, 491 @Nullable String[] args) { 492 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 493 494 IndentingPrintWriter ipw = new IndentingPrintWriter(pw); 495 mTimeDetectorStrategy.dump(ipw, args); 496 ipw.flush(); 497 } 498 499 @Override onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)500 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 501 String[] args, ShellCallback callback, ResultReceiver resultReceiver) { 502 new TimeDetectorShellCommand(this).exec( 503 this, in, out, err, args, callback, resultReceiver); 504 } 505 enforceSuggestTelephonyTimePermission()506 private void enforceSuggestTelephonyTimePermission() { 507 mContext.enforceCallingPermission( 508 android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE, 509 "suggest telephony time and time zone"); 510 } 511 enforceSuggestManualTimePermission()512 private void enforceSuggestManualTimePermission() { 513 mContext.enforceCallingPermission( 514 android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE, 515 "suggest manual time and time zone"); 516 } 517 enforceSuggestNetworkTimePermission()518 private void enforceSuggestNetworkTimePermission() { 519 mContext.enforceCallingPermission( 520 android.Manifest.permission.SET_TIME, 521 "suggest network time"); 522 } 523 enforceSuggestGnssTimePermission()524 private void enforceSuggestGnssTimePermission() { 525 mContext.enforceCallingPermission( 526 android.Manifest.permission.SET_TIME, 527 "suggest gnss time"); 528 } 529 enforceSuggestExternalTimePermission()530 private void enforceSuggestExternalTimePermission() { 531 // We don't expect a call from system server, so simply enforce calling permission. 532 mContext.enforceCallingPermission( 533 android.Manifest.permission.SUGGEST_EXTERNAL_TIME, 534 "suggest time from external source"); 535 } 536 enforceManageTimeDetectorPermission()537 private void enforceManageTimeDetectorPermission() { 538 mContext.enforceCallingPermission( 539 android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION, 540 "manage time and time zone detection"); 541 } 542 } 543