1 /*
2  * Copyright 2019 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.internal.telephony.nitz;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.app.time.UnixEpochTime;
22 import android.app.timedetector.TelephonyTimeSuggestion;
23 import android.app.timedetector.TimeDetector;
24 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
25 import android.app.timezonedetector.TimeZoneDetector;
26 import android.content.Context;
27 import android.os.SystemClock;
28 import android.util.LocalLog;
29 
30 import com.android.internal.telephony.Phone;
31 import com.android.internal.telephony.metrics.TelephonyMetrics;
32 import com.android.internal.util.IndentingPrintWriter;
33 
34 import java.io.PrintWriter;
35 import java.util.Objects;
36 
37 /**
38  * The real implementation of {@link TimeServiceHelper}.
39  */
40 public final class TimeServiceHelperImpl implements TimeServiceHelper {
41 
42     private final int mSlotIndex;
43     private final TimeDetector mTimeDetector;
44     private final TimeZoneDetector mTimeZoneDetector;
45 
46     private final LocalLog mTimeZoneLog = new LocalLog(32, false /* mUseLocalTimestamps */);
47     private final LocalLog mTimeLog = new LocalLog(32, false /* mUseLocalTimestamps */);
48 
49     /**
50      * Records the last time zone suggestion made. Used to avoid sending duplicate suggestions to
51      * the time zone service. The value can be {@code null} to indicate no previous suggestion has
52      * been made.
53      */
54     @NonNull
55     private TelephonyTimeZoneSuggestion mLastSuggestedTimeZone;
56 
TimeServiceHelperImpl(@onNull Phone phone)57     public TimeServiceHelperImpl(@NonNull Phone phone) {
58         mSlotIndex = phone.getPhoneId();
59         Context context = Objects.requireNonNull(phone.getContext());
60         mTimeDetector = Objects.requireNonNull(context.getSystemService(TimeDetector.class));
61         mTimeZoneDetector =
62                 Objects.requireNonNull(context.getSystemService(TimeZoneDetector.class));
63     }
64 
65     @Override
suggestDeviceTime(@onNull TelephonyTimeSuggestion timeSuggestion)66     public void suggestDeviceTime(@NonNull TelephonyTimeSuggestion timeSuggestion) {
67         mTimeLog.log("Sending time suggestion: " + timeSuggestion);
68 
69         Objects.requireNonNull(timeSuggestion);
70 
71         if (timeSuggestion.getUnixEpochTime() != null) {
72             UnixEpochTime unixEpochTime = timeSuggestion.getUnixEpochTime();
73             TelephonyMetrics.getInstance().writeNITZEvent(
74                     mSlotIndex, unixEpochTime.getUnixEpochTimeMillis());
75         }
76         mTimeDetector.suggestTelephonyTime(timeSuggestion);
77     }
78 
79     @Override
maybeSuggestDeviceTimeZone(@onNull TelephonyTimeZoneSuggestion newSuggestion)80     public void maybeSuggestDeviceTimeZone(@NonNull TelephonyTimeZoneSuggestion newSuggestion) {
81         Objects.requireNonNull(newSuggestion);
82 
83         TelephonyTimeZoneSuggestion oldSuggestion = mLastSuggestedTimeZone;
84         if (shouldSendNewTimeZoneSuggestion(oldSuggestion, newSuggestion)) {
85             mTimeZoneLog.log("Suggesting time zone update: " + newSuggestion);
86             mTimeZoneDetector.suggestTelephonyTimeZone(newSuggestion);
87             mLastSuggestedTimeZone = newSuggestion;
88         }
89     }
90 
shouldSendNewTimeZoneSuggestion( @ullable TelephonyTimeZoneSuggestion oldSuggestion, @NonNull TelephonyTimeZoneSuggestion newSuggestion)91     private static boolean shouldSendNewTimeZoneSuggestion(
92             @Nullable TelephonyTimeZoneSuggestion oldSuggestion,
93             @NonNull TelephonyTimeZoneSuggestion newSuggestion) {
94         if (oldSuggestion == null) {
95             // No previous suggestion.
96             return true;
97         }
98         // This code relies on PhoneTimeZoneSuggestion.equals() to only check meaningful fields.
99         return !Objects.equals(newSuggestion, oldSuggestion);
100     }
101 
102     @Override
dumpLogs(IndentingPrintWriter ipw)103     public void dumpLogs(IndentingPrintWriter ipw) {
104         ipw.println("TimeServiceHelperImpl:");
105         ipw.increaseIndent();
106         ipw.println("SystemClock.elapsedRealtime()=" + SystemClock.elapsedRealtime());
107         ipw.println("System.currentTimeMillis()=" + System.currentTimeMillis());
108 
109         ipw.println("Time Logs:");
110         ipw.increaseIndent();
111         mTimeLog.dump(ipw);
112         ipw.decreaseIndent();
113 
114         ipw.println("Time zone Logs:");
115         ipw.increaseIndent();
116         mTimeZoneLog.dump(ipw);
117         ipw.decreaseIndent();
118         ipw.decreaseIndent();
119     }
120 
121     @Override
dumpState(PrintWriter pw)122     public void dumpState(PrintWriter pw) {
123         pw.println(" TimeServiceHelperImpl.mLastSuggestedTimeZone=" + mLastSuggestedTimeZone);
124     }
125 }
126