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