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.content.Context; 22 import android.os.PowerManager; 23 import android.os.PowerManager.WakeLock; 24 import android.os.TimestampedValue; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 import com.android.internal.telephony.NitzData; 28 import com.android.internal.telephony.NitzStateMachine.DeviceState; 29 import com.android.internal.telephony.nitz.NitzStateMachineImpl.NitzSignalInputFilterPredicate; 30 import com.android.telephony.Rlog; 31 32 import java.util.Arrays; 33 import java.util.Objects; 34 35 /** 36 * A factory class for the {@link NitzSignalInputFilterPredicate} instance used by 37 * {@link NitzStateMachineImpl}. This class is exposed for testing and provides access to various 38 * internal components. 39 */ 40 @VisibleForTesting 41 public final class NitzSignalInputFilterPredicateFactory { 42 43 private static final String LOG_TAG = NitzStateMachineImpl.LOG_TAG; 44 private static final boolean DBG = NitzStateMachineImpl.DBG; 45 private static final String WAKELOCK_TAG = "NitzSignalInputFilterPredicateFactory"; 46 NitzSignalInputFilterPredicateFactory()47 private NitzSignalInputFilterPredicateFactory() {} 48 49 /** 50 * Returns the real {@link NitzSignalInputFilterPredicate} to use for NITZ signal input 51 * filtering. 52 */ 53 @NonNull create( @onNull Context context, @NonNull DeviceState deviceState)54 public static NitzSignalInputFilterPredicate create( 55 @NonNull Context context, @NonNull DeviceState deviceState) { 56 Objects.requireNonNull(context); 57 Objects.requireNonNull(deviceState); 58 59 TrivalentPredicate[] components = new TrivalentPredicate[] { 60 // Disables NITZ processing entirely: can return false or null. 61 createIgnoreNitzPropertyCheck(deviceState), 62 // Filters bad reference times from new signals: can return false or null. 63 createBogusElapsedRealtimeCheck(context, deviceState), 64 // Ensures oldSignal == null is always processed: can return true or null. 65 createNoOldSignalCheck(), 66 // Adds rate limiting: can return true or false. 67 createRateLimitCheck(deviceState), 68 }; 69 return new NitzSignalInputFilterPredicateImpl(components); 70 } 71 72 /** 73 * A filtering function that can give a {@code true} (must process), {@code false} (must not 74 * process) and a {@code null} (no opinion) response given a previous NITZ signal and a new 75 * signal. The previous signal may be {@code null} (unless ruled out by a prior 76 * {@link TrivalentPredicate}). 77 */ 78 @VisibleForTesting 79 @FunctionalInterface 80 public interface TrivalentPredicate { 81 82 /** 83 * See {@link TrivalentPredicate}. 84 */ 85 @Nullable mustProcessNitzSignal( @ullable TimestampedValue<NitzData> previousSignal, @NonNull TimestampedValue<NitzData> newSignal)86 Boolean mustProcessNitzSignal( 87 @Nullable TimestampedValue<NitzData> previousSignal, 88 @NonNull TimestampedValue<NitzData> newSignal); 89 } 90 91 /** 92 * Returns a {@link TrivalentPredicate} function that implements a check for the 93 * "gsm.ignore-nitz" Android system property. The function can return {@code false} or 94 * {@code null}. 95 */ 96 @VisibleForTesting 97 @NonNull createIgnoreNitzPropertyCheck( @onNull DeviceState deviceState)98 public static TrivalentPredicate createIgnoreNitzPropertyCheck( 99 @NonNull DeviceState deviceState) { 100 return (oldSignal, newSignal) -> { 101 boolean ignoreNitz = deviceState.getIgnoreNitz(); 102 if (ignoreNitz) { 103 if (DBG) { 104 Rlog.d(LOG_TAG, "mustProcessNitzSignal: Not processing NITZ signal because" 105 + " gsm.ignore-nitz is set"); 106 } 107 return false; 108 } 109 return null; 110 }; 111 } 112 113 /** 114 * Returns a {@link TrivalentPredicate} function that implements a check for a bad reference 115 * time associated with {@code newSignal}. The function can return {@code false} or 116 * {@code null}. 117 */ 118 @VisibleForTesting 119 @NonNull createBogusElapsedRealtimeCheck( @onNull Context context, @NonNull DeviceState deviceState)120 public static TrivalentPredicate createBogusElapsedRealtimeCheck( 121 @NonNull Context context, @NonNull DeviceState deviceState) { 122 PowerManager powerManager = 123 (PowerManager) context.getSystemService(Context.POWER_SERVICE); 124 final WakeLock wakeLock = 125 powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); 126 127 return (oldSignal, newSignal) -> { 128 Objects.requireNonNull(newSignal); 129 130 // Validate the newSignal to reject obviously bogus elapsedRealtime values. 131 try { 132 // Acquire the wake lock as we are reading the elapsed realtime clock below. 133 wakeLock.acquire(); 134 135 long elapsedRealtime = deviceState.elapsedRealtime(); 136 long millisSinceNitzReceived = elapsedRealtime - newSignal.getReferenceTimeMillis(); 137 if (millisSinceNitzReceived < 0 || millisSinceNitzReceived > Integer.MAX_VALUE) { 138 if (DBG) { 139 Rlog.d(LOG_TAG, "mustProcessNitzSignal: Not processing NITZ signal" 140 + " because unexpected elapsedRealtime=" + elapsedRealtime 141 + " nitzSignal=" + newSignal); 142 } 143 return false; 144 } 145 return null; 146 } finally { 147 wakeLock.release(); 148 } 149 }; 150 } 151 152 /** 153 * Returns a {@link TrivalentPredicate} function that implements a check for a {@code null} 154 * {@code oldSignal} (indicating there's no history). The function can return {@code true} 155 * or {@code null}. 156 */ 157 @VisibleForTesting 158 @NonNull 159 public static TrivalentPredicate createNoOldSignalCheck() { 160 // Always process a signal when there was no previous signal. 161 return (oldSignal, newSignal) -> oldSignal == null ? true : null; 162 } 163 164 /** 165 * Returns a {@link TrivalentPredicate} function that implements filtering using 166 * {@code oldSignal} and {@code newSignal}. The function can return {@code true} or 167 * {@code false} and so is intended as the final function in a chain. 168 * 169 * Function detail: if an NITZ signal received that is too similar to a previous one 170 * it should be disregarded if it's received within a configured time period. 171 * The general contract for {@link TrivalentPredicate} allows {@code previousSignal} to be 172 * {@code null}, but previous functions are expected to prevent it in this case. 173 */ 174 @VisibleForTesting 175 @NonNull 176 public static TrivalentPredicate createRateLimitCheck(@NonNull DeviceState deviceState) { 177 return new TrivalentPredicate() { 178 @Override 179 @NonNull 180 public Boolean mustProcessNitzSignal( 181 @NonNull TimestampedValue<NitzData> previousSignal, 182 @NonNull TimestampedValue<NitzData> newSignal) { 183 Objects.requireNonNull(newSignal); 184 Objects.requireNonNull(newSignal.getValue()); 185 Objects.requireNonNull(previousSignal); 186 Objects.requireNonNull(previousSignal.getValue()); 187 188 NitzData newNitzData = newSignal.getValue(); 189 NitzData previousNitzData = previousSignal.getValue(); 190 191 // Compare the discrete NitzData fields associated with local time offset. Any 192 // difference and we should process the signal regardless of how recent the last one 193 // was. 194 if (!offsetInfoIsTheSame(previousNitzData, newNitzData)) { 195 return true; 196 } 197 198 // Now check the continuous NitzData field (time) to see if it is sufficiently 199 // different. 200 int nitzUpdateSpacing = deviceState.getNitzUpdateSpacingMillis(); 201 int nitzUpdateDiff = deviceState.getNitzUpdateDiffMillis(); 202 203 // Calculate the elapsed time between the new signal and the last signal. 204 long elapsedRealtimeSinceLastSaved = newSignal.getReferenceTimeMillis() 205 - previousSignal.getReferenceTimeMillis(); 206 207 // Calculate the UTC difference between the time the two signals hold. 208 long utcTimeDifferenceMillis = newNitzData.getCurrentTimeInMillis() 209 - previousNitzData.getCurrentTimeInMillis(); 210 211 // Ideally the difference between elapsedRealtimeSinceLastSaved and 212 // utcTimeDifferenceMillis would be zero. 213 long millisGainedOrLost = Math 214 .abs(utcTimeDifferenceMillis - elapsedRealtimeSinceLastSaved); 215 216 if (elapsedRealtimeSinceLastSaved > nitzUpdateSpacing 217 || millisGainedOrLost > nitzUpdateDiff) { 218 return true; 219 } 220 221 if (DBG) { 222 Rlog.d(LOG_TAG, "mustProcessNitzSignal: NITZ signal filtered" 223 + " previousSignal=" + previousSignal 224 + ", newSignal=" + newSignal 225 + ", nitzUpdateSpacing=" + nitzUpdateSpacing 226 + ", nitzUpdateDiff=" + nitzUpdateDiff); 227 } 228 return false; 229 } 230 231 private boolean offsetInfoIsTheSame(NitzData one, NitzData two) { 232 return Objects.equals(two.getDstAdjustmentMillis(), one.getDstAdjustmentMillis()) 233 && Objects.equals( 234 two.getEmulatorHostTimeZone(), one.getEmulatorHostTimeZone()) 235 && two.getLocalOffsetMillis() == one.getLocalOffsetMillis(); 236 } 237 }; 238 } 239 240 /** 241 * An implementation of {@link NitzSignalInputFilterPredicate} that tries a series of 242 * {@link TrivalentPredicate} instances until one provides a {@code true} or {@code false} 243 * response indicating that the {@code newSignal} should be processed or not. If all return 244 * {@code null} then a default of {@code true} is returned. 245 */ 246 @VisibleForTesting 247 public static class NitzSignalInputFilterPredicateImpl 248 implements NitzSignalInputFilterPredicate { 249 250 @NonNull 251 private final TrivalentPredicate[] mComponents; 252 253 @VisibleForTesting 254 public NitzSignalInputFilterPredicateImpl(@NonNull TrivalentPredicate[] components) { 255 this.mComponents = Arrays.copyOf(components, components.length); 256 } 257 258 @Override 259 public boolean mustProcessNitzSignal(@Nullable TimestampedValue<NitzData> oldSignal, 260 @NonNull TimestampedValue<NitzData> newSignal) { 261 Objects.requireNonNull(newSignal); 262 263 for (TrivalentPredicate component : mComponents) { 264 Boolean result = component.mustProcessNitzSignal(oldSignal, newSignal); 265 if (result != null) { 266 return result; 267 } 268 } 269 // The default is to process. 270 return true; 271 } 272 } 273 } 274