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