1 /* 2 * Copyright (C) 2020 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.metrics; 18 19 import static android.text.format.DateUtils.DAY_IN_MILLIS; 20 21 import android.annotation.Nullable; 22 import android.content.Context; 23 import android.content.pm.PackageManager; 24 import android.os.Build; 25 import android.os.Handler; 26 import android.os.HandlerThread; 27 import android.telephony.TelephonyManager; 28 import android.telephony.TelephonyManager.NetworkTypeBitMask; 29 import android.util.SparseIntArray; 30 31 import com.android.internal.annotations.VisibleForTesting; 32 import com.android.internal.telephony.nano.PersistAtomsProto.CarrierIdMismatch; 33 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch; 34 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState; 35 import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession; 36 import com.android.internal.telephony.nano.PersistAtomsProto.GbaEvent; 37 import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerEvent; 38 import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerListenerEvent; 39 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationFeatureTagStats; 40 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationServiceDescStats; 41 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats; 42 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination; 43 import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms; 44 import com.android.internal.telephony.nano.PersistAtomsProto.NetworkRequestsV2; 45 import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingShortCodeSms; 46 import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms; 47 import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms; 48 import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent; 49 import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats; 50 import com.android.internal.telephony.nano.PersistAtomsProto.RcsClientProvisioningStats; 51 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteController; 52 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteIncomingDatagram; 53 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteOutgoingDatagram; 54 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteProvision; 55 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteSession; 56 import com.android.internal.telephony.nano.PersistAtomsProto.SatelliteSosMessageRecommender; 57 import com.android.internal.telephony.nano.PersistAtomsProto.SipDelegateStats; 58 import com.android.internal.telephony.nano.PersistAtomsProto.SipMessageResponse; 59 import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportFeatureTagStats; 60 import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportSession; 61 import com.android.internal.telephony.nano.PersistAtomsProto.UceEventStats; 62 import com.android.internal.telephony.nano.PersistAtomsProto.UnmeteredNetworks; 63 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage; 64 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession; 65 import com.android.internal.util.ArrayUtils; 66 import com.android.telephony.Rlog; 67 68 import java.io.FileOutputStream; 69 import java.io.IOException; 70 import java.nio.file.Files; 71 import java.nio.file.NoSuchFileException; 72 import java.security.SecureRandom; 73 import java.util.Arrays; 74 import java.util.Comparator; 75 import java.util.stream.IntStream; 76 77 /** 78 * Stores and aggregates metrics that should not be pulled at arbitrary frequency. 79 * 80 * <p>NOTE: while this class checks timestamp against {@code minIntervalMillis}, it is {@link 81 * MetricsCollector}'s responsibility to ensure {@code minIntervalMillis} is set correctly. 82 */ 83 public class PersistAtomsStorage { 84 private static final String TAG = PersistAtomsStorage.class.getSimpleName(); 85 86 /** Name of the file where cached statistics are saved to. */ 87 private static final String FILENAME = "persist_atoms.pb"; 88 89 /** Delay to store atoms to persistent storage to bundle multiple operations together. */ 90 private static final int SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS = 30000; 91 92 /** 93 * Delay to store atoms to persistent storage during pulls to avoid unnecessary operations. 94 * 95 * <p>This delay should be short to avoid duplicating atoms or losing pull timestamp in case of 96 * crash or power loss. 97 */ 98 private static final int SAVE_TO_FILE_DELAY_FOR_GET_MILLIS = 500; 99 100 /** Maximum number of call sessions to store between pulls. */ 101 private final int mMaxNumVoiceCallSessions; 102 103 /** 104 * Maximum number of SMS to store between pulls. Incoming messages and outgoing messages are 105 * counted separately. 106 */ 107 private final int mMaxNumSms; 108 109 /** 110 * Maximum number of carrier ID mismatch events stored on the device to avoid sending duplicated 111 * metrics. 112 */ 113 private final int mMaxNumCarrierIdMismatches; 114 115 /** Maximum number of data call sessions to store during pulls. */ 116 private final int mMaxNumDataCallSessions; 117 118 /** Maximum number of service states to store between pulls. */ 119 private final int mMaxNumCellularServiceStates; 120 121 /** Maximum number of data service switches to store between pulls. */ 122 private final int mMaxNumCellularDataSwitches; 123 124 /** Maximum number of IMS registration stats to store between pulls. */ 125 private final int mMaxNumImsRegistrationStats; 126 127 /** Maximum number of IMS registration terminations to store between pulls. */ 128 private final int mMaxNumImsRegistrationTerminations; 129 130 /** Maximum number of IMS Registration Feature Tags to store between pulls. */ 131 private final int mMaxNumImsRegistrationFeatureStats; 132 133 /** Maximum number of RCS Client Provisioning to store between pulls. */ 134 private final int mMaxNumRcsClientProvisioningStats; 135 136 /** Maximum number of RCS Acs Provisioning to store between pulls. */ 137 private final int mMaxNumRcsAcsProvisioningStats; 138 139 /** Maximum number of Sip Message Response to store between pulls. */ 140 private final int mMaxNumSipMessageResponseStats; 141 142 /** Maximum number of Sip Transport Session to store between pulls. */ 143 private final int mMaxNumSipTransportSessionStats; 144 145 /** Maximum number of Sip Delegate to store between pulls. */ 146 private final int mMaxNumSipDelegateStats; 147 148 /** Maximum number of Sip Transport Feature Tag to store between pulls. */ 149 private final int mMaxNumSipTransportFeatureTagStats; 150 151 /** Maximum number of Dedicated Bearer Listener Event to store between pulls. */ 152 private final int mMaxNumDedicatedBearerListenerEventStats; 153 154 /** Maximum number of Dedicated Bearer Event to store between pulls. */ 155 private final int mMaxNumDedicatedBearerEventStats; 156 157 /** Maximum number of IMS Registration Service Desc to store between pulls. */ 158 private final int mMaxNumImsRegistrationServiceDescStats; 159 160 /** Maximum number of UCE Event to store between pulls. */ 161 private final int mMaxNumUceEventStats; 162 163 /** Maximum number of Presence Notify Event to store between pulls. */ 164 private final int mMaxNumPresenceNotifyEventStats; 165 166 /** Maximum number of GBA Event to store between pulls. */ 167 private final int mMaxNumGbaEventStats; 168 169 /** Maximum number of outgoing short code sms to store between pulls. */ 170 private final int mMaxOutgoingShortCodeSms; 171 172 /** Maximum number of Satellite relevant stats to store between pulls. */ 173 private final int mMaxNumSatelliteStats; 174 private final int mMaxNumSatelliteControllerStats = 1; 175 176 /** Stores persist atoms and persist states of the puller. */ 177 @VisibleForTesting protected PersistAtoms mAtoms; 178 179 /** Aggregates RAT duration and call count. */ 180 private final VoiceCallRatTracker mVoiceCallRatTracker; 181 182 /** Whether atoms should be saved immediately, skipping the delay. */ 183 @VisibleForTesting protected boolean mSaveImmediately; 184 185 private final Context mContext; 186 private final Handler mHandler; 187 private final HandlerThread mHandlerThread; 188 private static final SecureRandom sRandom = new SecureRandom(); 189 190 private Runnable mSaveRunnable = 191 new Runnable() { 192 @Override 193 public void run() { 194 saveAtomsToFileNow(); 195 } 196 }; 197 PersistAtomsStorage(Context context)198 public PersistAtomsStorage(Context context) { 199 mContext = context; 200 201 if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_RAM_LOW)) { 202 Rlog.i(TAG, "Low RAM device"); 203 mMaxNumVoiceCallSessions = 10; 204 mMaxNumSms = 5; 205 mMaxNumCarrierIdMismatches = 8; 206 mMaxNumDataCallSessions = 5; 207 mMaxNumCellularServiceStates = 10; 208 mMaxNumCellularDataSwitches = 5; 209 mMaxNumImsRegistrationStats = 5; 210 mMaxNumImsRegistrationTerminations = 5; 211 mMaxNumImsRegistrationFeatureStats = 15; 212 mMaxNumRcsClientProvisioningStats = 5; 213 mMaxNumRcsAcsProvisioningStats = 5; 214 mMaxNumSipMessageResponseStats = 10; 215 mMaxNumSipTransportSessionStats = 10; 216 mMaxNumSipDelegateStats = 5; 217 mMaxNumSipTransportFeatureTagStats = 15; 218 mMaxNumDedicatedBearerListenerEventStats = 5; 219 mMaxNumDedicatedBearerEventStats = 5; 220 mMaxNumImsRegistrationServiceDescStats = 15; 221 mMaxNumUceEventStats = 5; 222 mMaxNumPresenceNotifyEventStats = 10; 223 mMaxNumGbaEventStats = 5; 224 mMaxOutgoingShortCodeSms = 5; 225 mMaxNumSatelliteStats = 5; 226 } else { 227 mMaxNumVoiceCallSessions = 50; 228 mMaxNumSms = 25; 229 mMaxNumCarrierIdMismatches = 40; 230 mMaxNumDataCallSessions = 15; 231 mMaxNumCellularServiceStates = 50; 232 mMaxNumCellularDataSwitches = 50; 233 mMaxNumImsRegistrationStats = 10; 234 mMaxNumImsRegistrationTerminations = 10; 235 mMaxNumImsRegistrationFeatureStats = 25; 236 mMaxNumRcsClientProvisioningStats = 10; 237 mMaxNumRcsAcsProvisioningStats = 10; 238 mMaxNumSipMessageResponseStats = 25; 239 mMaxNumSipTransportSessionStats = 25; 240 mMaxNumSipDelegateStats = 10; 241 mMaxNumSipTransportFeatureTagStats = 25; 242 mMaxNumDedicatedBearerListenerEventStats = 10; 243 mMaxNumDedicatedBearerEventStats = 10; 244 mMaxNumImsRegistrationServiceDescStats = 25; 245 mMaxNumUceEventStats = 25; 246 mMaxNumPresenceNotifyEventStats = 50; 247 mMaxNumGbaEventStats = 10; 248 mMaxOutgoingShortCodeSms = 10; 249 mMaxNumSatelliteStats = 15; 250 } 251 252 mAtoms = loadAtomsFromFile(); 253 mVoiceCallRatTracker = VoiceCallRatTracker.fromProto(mAtoms.voiceCallRatUsage); 254 255 mHandlerThread = new HandlerThread("PersistAtomsThread"); 256 mHandlerThread.start(); 257 mHandler = new Handler(mHandlerThread.getLooper()); 258 mSaveImmediately = false; 259 } 260 261 /** Adds a call to the storage. */ addVoiceCallSession(VoiceCallSession call)262 public synchronized void addVoiceCallSession(VoiceCallSession call) { 263 mAtoms.voiceCallSession = 264 insertAtRandomPlace(mAtoms.voiceCallSession, call, mMaxNumVoiceCallSessions); 265 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 266 267 Rlog.d(TAG, "Add new voice call session: " + call.toString()); 268 } 269 270 /** Adds RAT usages to the storage when a call session ends. */ addVoiceCallRatUsage(VoiceCallRatTracker ratUsages)271 public synchronized void addVoiceCallRatUsage(VoiceCallRatTracker ratUsages) { 272 mVoiceCallRatTracker.mergeWith(ratUsages); 273 mAtoms.voiceCallRatUsage = mVoiceCallRatTracker.toProto(); 274 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 275 } 276 277 /** Adds an incoming SMS to the storage. */ addIncomingSms(IncomingSms sms)278 public synchronized void addIncomingSms(IncomingSms sms) { 279 sms.hashCode = SmsStats.getSmsHashCode(sms); 280 mAtoms.incomingSms = insertAtRandomPlace(mAtoms.incomingSms, sms, mMaxNumSms); 281 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 282 283 // To be removed 284 Rlog.d(TAG, "Add new incoming SMS atom: " + sms.toString()); 285 } 286 287 /** Adds an outgoing SMS to the storage. */ addOutgoingSms(OutgoingSms sms)288 public synchronized void addOutgoingSms(OutgoingSms sms) { 289 sms.hashCode = SmsStats.getSmsHashCode(sms); 290 // Update the retry id, if needed, so that it's unique and larger than all 291 // previous ones. (this algorithm ignores the fact that some SMS atoms might 292 // be dropped due to limit in size of the array). 293 for (OutgoingSms storedSms : mAtoms.outgoingSms) { 294 if (storedSms.messageId == sms.messageId && storedSms.retryId >= sms.retryId) { 295 sms.retryId = storedSms.retryId + 1; 296 } 297 } 298 299 mAtoms.outgoingSms = insertAtRandomPlace(mAtoms.outgoingSms, sms, mMaxNumSms); 300 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 301 302 // To be removed 303 Rlog.d(TAG, "Add new outgoing SMS atom: " + sms.toString()); 304 } 305 306 /** Adds a service state to the storage, together with data service switch if any. */ addCellularServiceStateAndCellularDataServiceSwitch( CellularServiceState state, @Nullable CellularDataServiceSwitch serviceSwitch)307 public synchronized void addCellularServiceStateAndCellularDataServiceSwitch( 308 CellularServiceState state, @Nullable CellularDataServiceSwitch serviceSwitch) { 309 CellularServiceState existingState = find(state); 310 if (existingState != null) { 311 existingState.totalTimeMillis += state.totalTimeMillis; 312 existingState.lastUsedMillis = getWallTimeMillis(); 313 } else { 314 state.lastUsedMillis = getWallTimeMillis(); 315 mAtoms.cellularServiceState = 316 insertAtRandomPlace( 317 mAtoms.cellularServiceState, state, mMaxNumCellularServiceStates); 318 } 319 320 if (serviceSwitch != null) { 321 CellularDataServiceSwitch existingSwitch = find(serviceSwitch); 322 if (existingSwitch != null) { 323 existingSwitch.switchCount += serviceSwitch.switchCount; 324 existingSwitch.lastUsedMillis = getWallTimeMillis(); 325 } else { 326 serviceSwitch.lastUsedMillis = getWallTimeMillis(); 327 mAtoms.cellularDataServiceSwitch = 328 insertAtRandomPlace( 329 mAtoms.cellularDataServiceSwitch, 330 serviceSwitch, 331 mMaxNumCellularDataSwitches); 332 } 333 } 334 335 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 336 } 337 338 /** Adds a data call session to the storage. */ addDataCallSession(DataCallSession dataCall)339 public synchronized void addDataCallSession(DataCallSession dataCall) { 340 int index = findIndex(dataCall); 341 if (index >= 0) { 342 DataCallSession existingCall = mAtoms.dataCallSession[index]; 343 dataCall.ratSwitchCount += existingCall.ratSwitchCount; 344 dataCall.durationMinutes += existingCall.durationMinutes; 345 346 dataCall.handoverFailureCauses = IntStream.concat(Arrays.stream( 347 dataCall.handoverFailureCauses), 348 Arrays.stream(existingCall.handoverFailureCauses)) 349 .limit(DataCallSessionStats.SIZE_LIMIT_HANDOVER_FAILURES).toArray(); 350 dataCall.handoverFailureRat = IntStream.concat(Arrays.stream( 351 dataCall.handoverFailureRat), 352 Arrays.stream(existingCall.handoverFailureRat)) 353 .limit(DataCallSessionStats.SIZE_LIMIT_HANDOVER_FAILURES).toArray(); 354 355 mAtoms.dataCallSession[index] = dataCall; 356 } else { 357 mAtoms.dataCallSession = 358 insertAtRandomPlace(mAtoms.dataCallSession, dataCall, mMaxNumDataCallSessions); 359 } 360 361 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 362 } 363 364 /** 365 * Adds a new carrier ID mismatch event to the storage. 366 * 367 * @return true if the item was not present and was added to the persistent storage, false 368 * otherwise. 369 */ addCarrierIdMismatch(CarrierIdMismatch carrierIdMismatch)370 public synchronized boolean addCarrierIdMismatch(CarrierIdMismatch carrierIdMismatch) { 371 // Check if the details of the SIM cards are already present and in case return. 372 if (find(carrierIdMismatch) != null) { 373 return false; 374 } 375 // Add the new CarrierIdMismatch at the end of the array, so that the same atom will not be 376 // sent again in future. 377 if (mAtoms.carrierIdMismatch.length == mMaxNumCarrierIdMismatches) { 378 System.arraycopy( 379 mAtoms.carrierIdMismatch, 380 1, 381 mAtoms.carrierIdMismatch, 382 0, 383 mMaxNumCarrierIdMismatches - 1); 384 mAtoms.carrierIdMismatch[mMaxNumCarrierIdMismatches - 1] = carrierIdMismatch; 385 } else { 386 mAtoms.carrierIdMismatch = 387 ArrayUtils.appendElement( 388 CarrierIdMismatch.class, 389 mAtoms.carrierIdMismatch, 390 carrierIdMismatch, 391 true); 392 } 393 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 394 return true; 395 } 396 397 /** Adds IMS registration stats to the storage. */ addImsRegistrationStats(ImsRegistrationStats stats)398 public synchronized void addImsRegistrationStats(ImsRegistrationStats stats) { 399 ImsRegistrationStats existingStats = find(stats); 400 if (existingStats != null) { 401 existingStats.registeredMillis += stats.registeredMillis; 402 existingStats.voiceCapableMillis += stats.voiceCapableMillis; 403 existingStats.voiceAvailableMillis += stats.voiceAvailableMillis; 404 existingStats.smsCapableMillis += stats.smsCapableMillis; 405 existingStats.smsAvailableMillis += stats.smsAvailableMillis; 406 existingStats.videoCapableMillis += stats.videoCapableMillis; 407 existingStats.videoAvailableMillis += stats.videoAvailableMillis; 408 existingStats.utCapableMillis += stats.utCapableMillis; 409 existingStats.utAvailableMillis += stats.utAvailableMillis; 410 existingStats.lastUsedMillis = getWallTimeMillis(); 411 } else { 412 stats.lastUsedMillis = getWallTimeMillis(); 413 mAtoms.imsRegistrationStats = 414 insertAtRandomPlace( 415 mAtoms.imsRegistrationStats, stats, mMaxNumImsRegistrationStats); 416 } 417 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 418 } 419 420 /** Adds IMS registration termination to the storage. */ addImsRegistrationTermination(ImsRegistrationTermination termination)421 public synchronized void addImsRegistrationTermination(ImsRegistrationTermination termination) { 422 ImsRegistrationTermination existingTermination = find(termination); 423 if (existingTermination != null) { 424 existingTermination.count += termination.count; 425 existingTermination.lastUsedMillis = getWallTimeMillis(); 426 } else { 427 termination.lastUsedMillis = getWallTimeMillis(); 428 mAtoms.imsRegistrationTermination = 429 insertAtRandomPlace( 430 mAtoms.imsRegistrationTermination, 431 termination, 432 mMaxNumImsRegistrationTerminations); 433 } 434 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 435 } 436 437 /** 438 * Stores the version of the carrier ID matching table. 439 * 440 * @return true if the version is newer than last available version, false otherwise. 441 */ setCarrierIdTableVersion(int carrierIdTableVersion)442 public synchronized boolean setCarrierIdTableVersion(int carrierIdTableVersion) { 443 if (mAtoms.carrierIdTableVersion < carrierIdTableVersion) { 444 mAtoms.carrierIdTableVersion = carrierIdTableVersion; 445 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 446 return true; 447 } else { 448 return false; 449 } 450 } 451 452 /** 453 * Store the number of times auto data switch feature is toggled. 454 */ recordToggledAutoDataSwitch()455 public synchronized void recordToggledAutoDataSwitch() { 456 mAtoms.autoDataSwitchToggleCount++; 457 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 458 } 459 460 /** Adds a new {@link NetworkRequestsV2} to the storage. */ addNetworkRequestsV2(NetworkRequestsV2 networkRequests)461 public synchronized void addNetworkRequestsV2(NetworkRequestsV2 networkRequests) { 462 NetworkRequestsV2 existingMetrics = find(networkRequests); 463 if (existingMetrics != null) { 464 existingMetrics.requestCount += networkRequests.requestCount; 465 } else { 466 NetworkRequestsV2 newMetrics = new NetworkRequestsV2(); 467 newMetrics.capability = networkRequests.capability; 468 newMetrics.carrierId = networkRequests.carrierId; 469 newMetrics.requestCount = networkRequests.requestCount; 470 mAtoms.networkRequestsV2 = 471 ArrayUtils.appendElement( 472 NetworkRequestsV2.class, mAtoms.networkRequestsV2, newMetrics, true); 473 } 474 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 475 } 476 477 /** Adds a new {@link ImsRegistrationFeatureTagStats} to the storage. */ addImsRegistrationFeatureTagStats( ImsRegistrationFeatureTagStats stats)478 public synchronized void addImsRegistrationFeatureTagStats( 479 ImsRegistrationFeatureTagStats stats) { 480 ImsRegistrationFeatureTagStats existingStats = find(stats); 481 if (existingStats != null) { 482 existingStats.registeredMillis += stats.registeredMillis; 483 } else { 484 mAtoms.imsRegistrationFeatureTagStats = 485 insertAtRandomPlace(mAtoms.imsRegistrationFeatureTagStats, 486 stats, mMaxNumImsRegistrationFeatureStats); 487 } 488 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 489 } 490 491 /** Adds a new {@link RcsClientProvisioningStats} to the storage. */ addRcsClientProvisioningStats(RcsClientProvisioningStats stats)492 public synchronized void addRcsClientProvisioningStats(RcsClientProvisioningStats stats) { 493 RcsClientProvisioningStats existingStats = find(stats); 494 if (existingStats != null) { 495 existingStats.count += 1; 496 } else { 497 mAtoms.rcsClientProvisioningStats = 498 insertAtRandomPlace(mAtoms.rcsClientProvisioningStats, stats, 499 mMaxNumRcsClientProvisioningStats); 500 } 501 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 502 } 503 504 /** Adds a new {@link RcsAcsProvisioningStats} to the storage. */ addRcsAcsProvisioningStats(RcsAcsProvisioningStats stats)505 public synchronized void addRcsAcsProvisioningStats(RcsAcsProvisioningStats stats) { 506 RcsAcsProvisioningStats existingStats = find(stats); 507 if (existingStats != null) { 508 existingStats.count += 1; 509 existingStats.stateTimerMillis += stats.stateTimerMillis; 510 } else { 511 // prevent that wrong count from caller effects total count 512 stats.count = 1; 513 mAtoms.rcsAcsProvisioningStats = 514 insertAtRandomPlace(mAtoms.rcsAcsProvisioningStats, stats, 515 mMaxNumRcsAcsProvisioningStats); 516 } 517 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 518 } 519 520 /** Adds a new {@link SipDelegateStats} to the storage. */ addSipDelegateStats(SipDelegateStats stats)521 public synchronized void addSipDelegateStats(SipDelegateStats stats) { 522 mAtoms.sipDelegateStats = insertAtRandomPlace(mAtoms.sipDelegateStats, stats, 523 mMaxNumSipDelegateStats); 524 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 525 } 526 527 /** Adds a new {@link SipTransportFeatureTagStats} to the storage. */ addSipTransportFeatureTagStats(SipTransportFeatureTagStats stats)528 public synchronized void addSipTransportFeatureTagStats(SipTransportFeatureTagStats stats) { 529 SipTransportFeatureTagStats lastStat = find(stats); 530 if (lastStat != null) { 531 lastStat.associatedMillis += stats.associatedMillis; 532 } else { 533 mAtoms.sipTransportFeatureTagStats = 534 insertAtRandomPlace(mAtoms.sipTransportFeatureTagStats, stats, 535 mMaxNumSipTransportFeatureTagStats); 536 } 537 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 538 } 539 540 /** Adds a new {@link SipMessageResponse} to the storage. */ addSipMessageResponse(SipMessageResponse stats)541 public synchronized void addSipMessageResponse(SipMessageResponse stats) { 542 SipMessageResponse existingStats = find(stats); 543 if (existingStats != null) { 544 existingStats.count += 1; 545 } else { 546 mAtoms.sipMessageResponse = insertAtRandomPlace(mAtoms.sipMessageResponse, stats, 547 mMaxNumSipMessageResponseStats); 548 } 549 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 550 } 551 552 /** Adds a new {@link SipTransportSession} to the storage. */ addCompleteSipTransportSession(SipTransportSession stats)553 public synchronized void addCompleteSipTransportSession(SipTransportSession stats) { 554 SipTransportSession existingStats = find(stats); 555 if (existingStats != null) { 556 existingStats.sessionCount += 1; 557 if (stats.isEndedGracefully) { 558 existingStats.endedGracefullyCount += 1; 559 } 560 } else { 561 mAtoms.sipTransportSession = 562 insertAtRandomPlace(mAtoms.sipTransportSession, stats, 563 mMaxNumSipTransportSessionStats); 564 } 565 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 566 } 567 568 /** Adds a new {@link ImsDedicatedBearerListenerEvent} to the storage. */ addImsDedicatedBearerListenerEvent( ImsDedicatedBearerListenerEvent stats)569 public synchronized void addImsDedicatedBearerListenerEvent( 570 ImsDedicatedBearerListenerEvent stats) { 571 ImsDedicatedBearerListenerEvent existingStats = find(stats); 572 if (existingStats != null) { 573 existingStats.eventCount += 1; 574 } else { 575 mAtoms.imsDedicatedBearerListenerEvent = 576 insertAtRandomPlace(mAtoms.imsDedicatedBearerListenerEvent, 577 stats, mMaxNumDedicatedBearerListenerEventStats); 578 } 579 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 580 } 581 582 /** Adds a new {@link ImsDedicatedBearerEvent} to the storage. */ addImsDedicatedBearerEvent(ImsDedicatedBearerEvent stats)583 public synchronized void addImsDedicatedBearerEvent(ImsDedicatedBearerEvent stats) { 584 ImsDedicatedBearerEvent existingStats = find(stats); 585 if (existingStats != null) { 586 existingStats.count += 1; 587 } else { 588 mAtoms.imsDedicatedBearerEvent = 589 insertAtRandomPlace(mAtoms.imsDedicatedBearerEvent, stats, 590 mMaxNumDedicatedBearerEventStats); 591 } 592 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 593 } 594 595 /** Adds a new {@link ImsRegistrationServiceDescStats} to the storage. */ addImsRegistrationServiceDescStats( ImsRegistrationServiceDescStats stats)596 public synchronized void addImsRegistrationServiceDescStats( 597 ImsRegistrationServiceDescStats stats) { 598 ImsRegistrationServiceDescStats existingStats = find(stats); 599 if (existingStats != null) { 600 existingStats.publishedMillis += stats.publishedMillis; 601 } else { 602 mAtoms.imsRegistrationServiceDescStats = 603 insertAtRandomPlace(mAtoms.imsRegistrationServiceDescStats, 604 stats, mMaxNumImsRegistrationServiceDescStats); 605 } 606 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 607 } 608 609 /** Adds a new {@link UceEventStats} to the storage. */ addUceEventStats(UceEventStats stats)610 public synchronized void addUceEventStats(UceEventStats stats) { 611 UceEventStats existingStats = find(stats); 612 if (existingStats != null) { 613 existingStats.count += 1; 614 } else { 615 mAtoms.uceEventStats = 616 insertAtRandomPlace(mAtoms.uceEventStats, stats, mMaxNumUceEventStats); 617 } 618 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 619 } 620 621 /** Adds a new {@link PresenceNotifyEvent} to the storage. */ addPresenceNotifyEvent(PresenceNotifyEvent stats)622 public synchronized void addPresenceNotifyEvent(PresenceNotifyEvent stats) { 623 PresenceNotifyEvent existingStats = find(stats); 624 if (existingStats != null) { 625 existingStats.rcsCapsCount += stats.rcsCapsCount; 626 existingStats.mmtelCapsCount += stats.mmtelCapsCount; 627 existingStats.noCapsCount += stats.noCapsCount; 628 existingStats.count += stats.count; 629 } else { 630 mAtoms.presenceNotifyEvent = 631 insertAtRandomPlace(mAtoms.presenceNotifyEvent, stats, 632 mMaxNumPresenceNotifyEventStats); 633 } 634 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 635 } 636 637 /** Adds a new {@link GbaEvent} to the storage. */ addGbaEvent(GbaEvent stats)638 public synchronized void addGbaEvent(GbaEvent stats) { 639 GbaEvent existingStats = find(stats); 640 if (existingStats != null) { 641 existingStats.count += 1; 642 } else { 643 mAtoms.gbaEvent = 644 insertAtRandomPlace(mAtoms.gbaEvent, stats, mMaxNumGbaEventStats); 645 } 646 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 647 } 648 649 /** 650 * Sets the unmetered networks bitmask for a given phone id. If the carrier id 651 * doesn't match the existing UnmeteredNetworks' carrier id, the bitmask is 652 * first reset to 0. 653 */ addUnmeteredNetworks( int phoneId, int carrierId, @NetworkTypeBitMask long bitmask)654 public synchronized void addUnmeteredNetworks( 655 int phoneId, int carrierId, @NetworkTypeBitMask long bitmask) { 656 UnmeteredNetworks stats = findUnmeteredNetworks(phoneId); 657 boolean needToSave = true; 658 if (stats == null) { 659 stats = new UnmeteredNetworks(); 660 stats.phoneId = phoneId; 661 stats.carrierId = carrierId; 662 stats.unmeteredNetworksBitmask = bitmask; 663 mAtoms.unmeteredNetworks = 664 ArrayUtils.appendElement( 665 UnmeteredNetworks.class, mAtoms.unmeteredNetworks, stats, true); 666 } else { 667 // Reset the bitmask to 0 if carrier id doesn't match. 668 if (stats.carrierId != carrierId) { 669 stats.carrierId = carrierId; 670 stats.unmeteredNetworksBitmask = 0; 671 } 672 if ((stats.unmeteredNetworksBitmask | bitmask) != stats.unmeteredNetworksBitmask) { 673 stats.unmeteredNetworksBitmask |= bitmask; 674 } else { 675 needToSave = false; 676 } 677 } 678 // Only save if something changes. 679 if (needToSave) { 680 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 681 } 682 } 683 684 /** Adds an outgoing short code sms to the storage. */ addOutgoingShortCodeSms(OutgoingShortCodeSms shortCodeSms)685 public synchronized void addOutgoingShortCodeSms(OutgoingShortCodeSms shortCodeSms) { 686 OutgoingShortCodeSms existingOutgoingShortCodeSms = find(shortCodeSms); 687 if (existingOutgoingShortCodeSms != null) { 688 existingOutgoingShortCodeSms.shortCodeSmsCount += 1; 689 } else { 690 mAtoms.outgoingShortCodeSms = insertAtRandomPlace(mAtoms.outgoingShortCodeSms, 691 shortCodeSms, mMaxOutgoingShortCodeSms); 692 } 693 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 694 } 695 696 /** Adds a new {@link SatelliteController} to the storage. */ addSatelliteControllerStats(SatelliteController stats)697 public synchronized void addSatelliteControllerStats(SatelliteController stats) { 698 // SatelliteController is a single data point 699 SatelliteController[] atomArray = mAtoms.satelliteController; 700 if (atomArray == null || atomArray.length == 0) { 701 atomArray = new SatelliteController[] {new SatelliteController()}; 702 } 703 704 SatelliteController atom = atomArray[0]; 705 atom.countOfSatelliteServiceEnablementsSuccess 706 += stats.countOfSatelliteServiceEnablementsSuccess; 707 atom.countOfSatelliteServiceEnablementsFail 708 += stats.countOfSatelliteServiceEnablementsFail; 709 atom.countOfOutgoingDatagramSuccess 710 += stats.countOfOutgoingDatagramSuccess; 711 atom.countOfOutgoingDatagramFail 712 += stats.countOfOutgoingDatagramFail; 713 atom.countOfIncomingDatagramSuccess 714 += stats.countOfIncomingDatagramSuccess; 715 atom.countOfIncomingDatagramFail 716 += stats.countOfIncomingDatagramFail; 717 atom.countOfDatagramTypeSosSmsSuccess 718 += stats.countOfDatagramTypeSosSmsSuccess; 719 atom.countOfDatagramTypeSosSmsFail 720 += stats.countOfDatagramTypeSosSmsFail; 721 atom.countOfDatagramTypeLocationSharingSuccess 722 += stats.countOfDatagramTypeLocationSharingSuccess; 723 atom.countOfDatagramTypeLocationSharingFail 724 += stats.countOfDatagramTypeLocationSharingFail; 725 atom.countOfProvisionSuccess 726 += stats.countOfProvisionSuccess; 727 atom.countOfProvisionFail 728 += stats.countOfProvisionFail; 729 atom.countOfDeprovisionSuccess 730 += stats.countOfDeprovisionSuccess; 731 atom.countOfDeprovisionFail 732 += stats.countOfDeprovisionFail; 733 atom.totalServiceUptimeSec 734 += stats.totalServiceUptimeSec; 735 atom.totalBatteryConsumptionPercent 736 += stats.totalBatteryConsumptionPercent; 737 atom.totalBatteryChargedTimeSec 738 += stats.totalBatteryChargedTimeSec; 739 740 mAtoms.satelliteController = atomArray; 741 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 742 } 743 744 /** Adds a new {@link SatelliteSession} to the storage. */ addSatelliteSessionStats(SatelliteSession stats)745 public synchronized void addSatelliteSessionStats(SatelliteSession stats) { 746 SatelliteSession existingStats = find(stats); 747 if (existingStats != null) { 748 existingStats.count += 1; 749 } else { 750 mAtoms.satelliteSession = 751 insertAtRandomPlace(mAtoms.satelliteSession, stats, mMaxNumSatelliteStats); 752 } 753 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 754 } 755 756 /** Adds a new {@link SatelliteIncomingDatagram} to the storage. */ addSatelliteIncomingDatagramStats(SatelliteIncomingDatagram stats)757 public synchronized void addSatelliteIncomingDatagramStats(SatelliteIncomingDatagram stats) { 758 mAtoms.satelliteIncomingDatagram = 759 insertAtRandomPlace(mAtoms.satelliteIncomingDatagram, stats, mMaxNumSatelliteStats); 760 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 761 } 762 763 /** Adds a new {@link SatelliteOutgoingDatagram} to the storage. */ addSatelliteOutgoingDatagramStats(SatelliteOutgoingDatagram stats)764 public synchronized void addSatelliteOutgoingDatagramStats(SatelliteOutgoingDatagram stats) { 765 mAtoms.satelliteOutgoingDatagram = 766 insertAtRandomPlace(mAtoms.satelliteOutgoingDatagram, stats, mMaxNumSatelliteStats); 767 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 768 } 769 770 /** Adds a new {@link SatelliteProvision} to the storage. */ addSatelliteProvisionStats(SatelliteProvision stats)771 public synchronized void addSatelliteProvisionStats(SatelliteProvision stats) { 772 mAtoms.satelliteProvision = 773 insertAtRandomPlace(mAtoms.satelliteProvision, stats, mMaxNumSatelliteStats); 774 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 775 } 776 777 /** Adds a new {@link SatelliteSosMessageRecommender} to the storage. */ addSatelliteSosMessageRecommenderStats( SatelliteSosMessageRecommender stats)778 public synchronized void addSatelliteSosMessageRecommenderStats( 779 SatelliteSosMessageRecommender stats) { 780 SatelliteSosMessageRecommender existingStats = find(stats); 781 if (existingStats != null) { 782 existingStats.count += 1; 783 } else { 784 mAtoms.satelliteSosMessageRecommender = 785 insertAtRandomPlace(mAtoms.satelliteSosMessageRecommender, stats, 786 mMaxNumSatelliteStats); 787 } 788 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 789 } 790 791 /** 792 * Returns and clears the voice call sessions if last pulled longer than {@code 793 * minIntervalMillis} ago, otherwise returns {@code null}. 794 */ 795 @Nullable getVoiceCallSessions(long minIntervalMillis)796 public synchronized VoiceCallSession[] getVoiceCallSessions(long minIntervalMillis) { 797 if (getWallTimeMillis() - mAtoms.voiceCallSessionPullTimestampMillis > minIntervalMillis) { 798 mAtoms.voiceCallSessionPullTimestampMillis = getWallTimeMillis(); 799 VoiceCallSession[] previousCalls = mAtoms.voiceCallSession; 800 mAtoms.voiceCallSession = new VoiceCallSession[0]; 801 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 802 return previousCalls; 803 } else { 804 return null; 805 } 806 } 807 808 /** 809 * Returns and clears the voice call RAT usages if last pulled longer than {@code 810 * minIntervalMillis} ago, otherwise returns {@code null}. 811 */ 812 @Nullable getVoiceCallRatUsages(long minIntervalMillis)813 public synchronized VoiceCallRatUsage[] getVoiceCallRatUsages(long minIntervalMillis) { 814 if (getWallTimeMillis() - mAtoms.voiceCallRatUsagePullTimestampMillis > minIntervalMillis) { 815 mAtoms.voiceCallRatUsagePullTimestampMillis = getWallTimeMillis(); 816 VoiceCallRatUsage[] previousUsages = mAtoms.voiceCallRatUsage; 817 mVoiceCallRatTracker.clear(); 818 mAtoms.voiceCallRatUsage = new VoiceCallRatUsage[0]; 819 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 820 return previousUsages; 821 } else { 822 return null; 823 } 824 } 825 826 /** 827 * Returns and clears the incoming SMS if last pulled longer than {@code minIntervalMillis} ago, 828 * otherwise returns {@code null}. 829 */ 830 @Nullable getIncomingSms(long minIntervalMillis)831 public synchronized IncomingSms[] getIncomingSms(long minIntervalMillis) { 832 if (getWallTimeMillis() - mAtoms.incomingSmsPullTimestampMillis > minIntervalMillis) { 833 mAtoms.incomingSmsPullTimestampMillis = getWallTimeMillis(); 834 IncomingSms[] previousIncomingSms = mAtoms.incomingSms; 835 mAtoms.incomingSms = new IncomingSms[0]; 836 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 837 return previousIncomingSms; 838 } else { 839 return null; 840 } 841 } 842 843 /** 844 * Returns and clears the outgoing SMS if last pulled longer than {@code minIntervalMillis} ago, 845 * otherwise returns {@code null}. 846 */ 847 @Nullable getOutgoingSms(long minIntervalMillis)848 public synchronized OutgoingSms[] getOutgoingSms(long minIntervalMillis) { 849 if (getWallTimeMillis() - mAtoms.outgoingSmsPullTimestampMillis > minIntervalMillis) { 850 mAtoms.outgoingSmsPullTimestampMillis = getWallTimeMillis(); 851 OutgoingSms[] previousOutgoingSms = mAtoms.outgoingSms; 852 mAtoms.outgoingSms = new OutgoingSms[0]; 853 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 854 return previousOutgoingSms; 855 } else { 856 return null; 857 } 858 } 859 860 /** 861 * Returns and clears the data call session if last pulled longer than {@code minIntervalMillis} 862 * ago, otherwise returns {@code null}. 863 */ 864 @Nullable getDataCallSessions(long minIntervalMillis)865 public synchronized DataCallSession[] getDataCallSessions(long minIntervalMillis) { 866 if (getWallTimeMillis() - mAtoms.dataCallSessionPullTimestampMillis > minIntervalMillis) { 867 mAtoms.dataCallSessionPullTimestampMillis = getWallTimeMillis(); 868 DataCallSession[] previousDataCallSession = mAtoms.dataCallSession; 869 mAtoms.dataCallSession = new DataCallSession[0]; 870 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 871 for (DataCallSession dataCallSession : previousDataCallSession) { 872 // sort to de-correlate any potential pattern for UII concern 873 sortBaseOnArray(dataCallSession.handoverFailureCauses, 874 dataCallSession.handoverFailureRat); 875 } 876 return previousDataCallSession; 877 } else { 878 return null; 879 } 880 } 881 882 /** 883 * Sort the other array base on the natural order of the primary array. Both arrays will be 884 * sorted in-place. 885 * @param primary The primary array to be sorted. 886 * @param other The other array to be sorted in the order of primary array. 887 */ sortBaseOnArray(int[] primary, int[] other)888 private void sortBaseOnArray(int[] primary, int[] other) { 889 if (other.length != primary.length) return; 890 int[] index = IntStream.range(0, primary.length).boxed() 891 .sorted(Comparator.comparingInt(i -> primary[i])) 892 .mapToInt(Integer::intValue) 893 .toArray(); 894 int[] primaryCopy = Arrays.copyOf(primary, primary.length); 895 int[] otherCopy = Arrays.copyOf(other, other.length); 896 for (int i = 0; i < index.length; i++) { 897 primary[i] = primaryCopy[index[i]]; 898 other[i] = otherCopy[index[i]]; 899 } 900 } 901 902 903 /** 904 * Returns and clears the service state durations if last pulled longer than {@code 905 * minIntervalMillis} ago, otherwise returns {@code null}. 906 */ 907 @Nullable getCellularServiceStates(long minIntervalMillis)908 public synchronized CellularServiceState[] getCellularServiceStates(long minIntervalMillis) { 909 if (getWallTimeMillis() - mAtoms.cellularServiceStatePullTimestampMillis 910 > minIntervalMillis) { 911 mAtoms.cellularServiceStatePullTimestampMillis = getWallTimeMillis(); 912 CellularServiceState[] previousStates = mAtoms.cellularServiceState; 913 Arrays.stream(previousStates).forEach(state -> state.lastUsedMillis = 0L); 914 mAtoms.cellularServiceState = new CellularServiceState[0]; 915 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 916 return previousStates; 917 } else { 918 return null; 919 } 920 } 921 922 /** 923 * Returns and clears the service state durations if last pulled longer than {@code 924 * minIntervalMillis} ago, otherwise returns {@code null}. 925 */ 926 @Nullable getCellularDataServiceSwitches( long minIntervalMillis)927 public synchronized CellularDataServiceSwitch[] getCellularDataServiceSwitches( 928 long minIntervalMillis) { 929 if (getWallTimeMillis() - mAtoms.cellularDataServiceSwitchPullTimestampMillis 930 > minIntervalMillis) { 931 mAtoms.cellularDataServiceSwitchPullTimestampMillis = getWallTimeMillis(); 932 CellularDataServiceSwitch[] previousSwitches = mAtoms.cellularDataServiceSwitch; 933 Arrays.stream(previousSwitches) 934 .forEach(serviceSwitch -> serviceSwitch.lastUsedMillis = 0L); 935 mAtoms.cellularDataServiceSwitch = new CellularDataServiceSwitch[0]; 936 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 937 return previousSwitches; 938 } else { 939 return null; 940 } 941 } 942 943 /** 944 * Returns and clears the IMS registration statistics normalized to 24h cycle if last 945 * pulled longer than {@code minIntervalMillis} ago, otherwise returns {@code null}. 946 */ 947 @Nullable getImsRegistrationStats(long minIntervalMillis)948 public synchronized ImsRegistrationStats[] getImsRegistrationStats(long minIntervalMillis) { 949 long intervalMillis = 950 getWallTimeMillis() - mAtoms.imsRegistrationStatsPullTimestampMillis; 951 if (intervalMillis > minIntervalMillis) { 952 mAtoms.imsRegistrationStatsPullTimestampMillis = getWallTimeMillis(); 953 ImsRegistrationStats[] previousStats = mAtoms.imsRegistrationStats; 954 Arrays.stream(previousStats).forEach(stats -> stats.lastUsedMillis = 0L); 955 mAtoms.imsRegistrationStats = new ImsRegistrationStats[0]; 956 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 957 return normalizeData(previousStats, intervalMillis); 958 } else { 959 return null; 960 } 961 } 962 963 /** 964 * Returns and clears the IMS registration terminations if last pulled longer than {@code 965 * minIntervalMillis} ago, otherwise returns {@code null}. 966 */ 967 @Nullable getImsRegistrationTerminations( long minIntervalMillis)968 public synchronized ImsRegistrationTermination[] getImsRegistrationTerminations( 969 long minIntervalMillis) { 970 if (getWallTimeMillis() - mAtoms.imsRegistrationTerminationPullTimestampMillis 971 > minIntervalMillis) { 972 mAtoms.imsRegistrationTerminationPullTimestampMillis = getWallTimeMillis(); 973 ImsRegistrationTermination[] previousTerminations = mAtoms.imsRegistrationTermination; 974 Arrays.stream(previousTerminations) 975 .forEach(termination -> termination.lastUsedMillis = 0L); 976 mAtoms.imsRegistrationTermination = new ImsRegistrationTermination[0]; 977 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 978 return previousTerminations; 979 } else { 980 return null; 981 } 982 } 983 984 /** 985 * Returns and clears the network requests if last pulled longer than {@code 986 * minIntervalMillis} ago, otherwise returns {@code null}. 987 */ 988 @Nullable getNetworkRequestsV2(long minIntervalMillis)989 public synchronized NetworkRequestsV2[] getNetworkRequestsV2(long minIntervalMillis) { 990 if (getWallTimeMillis() - mAtoms.networkRequestsV2PullTimestampMillis > minIntervalMillis) { 991 mAtoms.networkRequestsV2PullTimestampMillis = getWallTimeMillis(); 992 NetworkRequestsV2[] previousNetworkRequests = mAtoms.networkRequestsV2; 993 mAtoms.networkRequestsV2 = new NetworkRequestsV2[0]; 994 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 995 return previousNetworkRequests; 996 } else { 997 return null; 998 } 999 } 1000 1001 /** @return the number of times auto data switch mobile data policy is toggled. */ getAutoDataSwitchToggleCount()1002 public synchronized int getAutoDataSwitchToggleCount() { 1003 int count = mAtoms.autoDataSwitchToggleCount; 1004 if (count > 0) { 1005 mAtoms.autoDataSwitchToggleCount = 0; 1006 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1007 } 1008 return count; 1009 } 1010 1011 /** 1012 * Returns and clears the ImsRegistrationFeatureTagStats if last pulled longer than 1013 * {@code minIntervalMillis} ago, otherwise returns {@code null}. 1014 */ 1015 @Nullable getImsRegistrationFeatureTagStats( long minIntervalMillis)1016 public synchronized ImsRegistrationFeatureTagStats[] getImsRegistrationFeatureTagStats( 1017 long minIntervalMillis) { 1018 long intervalMillis = 1019 getWallTimeMillis() - mAtoms.rcsAcsProvisioningStatsPullTimestampMillis; 1020 if (intervalMillis > minIntervalMillis) { 1021 mAtoms.imsRegistrationFeatureTagStatsPullTimestampMillis = getWallTimeMillis(); 1022 ImsRegistrationFeatureTagStats[] previousStats = 1023 mAtoms.imsRegistrationFeatureTagStats; 1024 mAtoms.imsRegistrationFeatureTagStats = new ImsRegistrationFeatureTagStats[0]; 1025 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1026 return previousStats; 1027 } else { 1028 return null; 1029 } 1030 } 1031 1032 /** 1033 * Returns and clears the RcsClientProvisioningStats if last pulled longer than {@code 1034 * minIntervalMillis} ago, otherwise returns {@code null}. 1035 */ 1036 @Nullable getRcsClientProvisioningStats( long minIntervalMillis)1037 public synchronized RcsClientProvisioningStats[] getRcsClientProvisioningStats( 1038 long minIntervalMillis) { 1039 if (getWallTimeMillis() - mAtoms.rcsClientProvisioningStatsPullTimestampMillis 1040 > minIntervalMillis) { 1041 mAtoms.rcsClientProvisioningStatsPullTimestampMillis = getWallTimeMillis(); 1042 RcsClientProvisioningStats[] previousStats = mAtoms.rcsClientProvisioningStats; 1043 mAtoms.rcsClientProvisioningStats = new RcsClientProvisioningStats[0]; 1044 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1045 return previousStats; 1046 } else { 1047 return null; 1048 } 1049 } 1050 1051 /** 1052 * Returns and clears the RcsAcsProvisioningStats normalized to 24h cycle if last pulled 1053 * longer than {@code minIntervalMillis} ago, otherwise returns {@code null}. 1054 */ 1055 @Nullable getRcsAcsProvisioningStats( long minIntervalMillis)1056 public synchronized RcsAcsProvisioningStats[] getRcsAcsProvisioningStats( 1057 long minIntervalMillis) { 1058 long intervalMillis = 1059 getWallTimeMillis() - mAtoms.rcsAcsProvisioningStatsPullTimestampMillis; 1060 if (intervalMillis > minIntervalMillis) { 1061 mAtoms.rcsAcsProvisioningStatsPullTimestampMillis = getWallTimeMillis(); 1062 RcsAcsProvisioningStats[] previousStats = mAtoms.rcsAcsProvisioningStats; 1063 1064 for (RcsAcsProvisioningStats stat: previousStats) { 1065 // in case pull interval is greater than 24H, normalize it as of one day interval 1066 if (intervalMillis > DAY_IN_MILLIS) { 1067 stat.stateTimerMillis = normalizeDurationTo24H(stat.stateTimerMillis, 1068 intervalMillis); 1069 } 1070 } 1071 1072 mAtoms.rcsAcsProvisioningStats = new RcsAcsProvisioningStats[0]; 1073 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1074 return previousStats; 1075 } else { 1076 return null; 1077 } 1078 } 1079 1080 /** 1081 * Returns and clears the SipDelegateStats if last pulled longer than {@code 1082 * minIntervalMillis} ago, otherwise returns {@code null}. 1083 */ 1084 @Nullable getSipDelegateStats(long minIntervalMillis)1085 public synchronized SipDelegateStats[] getSipDelegateStats(long minIntervalMillis) { 1086 long intervalMillis = getWallTimeMillis() - mAtoms.sipDelegateStatsPullTimestampMillis; 1087 if (intervalMillis > minIntervalMillis) { 1088 mAtoms.sipDelegateStatsPullTimestampMillis = getWallTimeMillis(); 1089 SipDelegateStats[] previousStats = mAtoms.sipDelegateStats; 1090 1091 for (SipDelegateStats stat: previousStats) { 1092 // in case pull interval is greater than 24H, normalize it as of one day interval 1093 if (intervalMillis > DAY_IN_MILLIS) { 1094 stat.uptimeMillis = normalizeDurationTo24H(stat.uptimeMillis, 1095 intervalMillis); 1096 } 1097 } 1098 1099 mAtoms.sipDelegateStats = new SipDelegateStats[0]; 1100 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1101 return previousStats; 1102 } else { 1103 return null; 1104 } 1105 } 1106 1107 /** 1108 * Returns and clears the SipTransportFeatureTagStats if last pulled longer than {@code 1109 * minIntervalMillis} ago, otherwise returns {@code null}. 1110 */ 1111 @Nullable getSipTransportFeatureTagStats( long minIntervalMillis)1112 public synchronized SipTransportFeatureTagStats[] getSipTransportFeatureTagStats( 1113 long minIntervalMillis) { 1114 long intervalMillis = 1115 getWallTimeMillis() - mAtoms.sipTransportFeatureTagStatsPullTimestampMillis; 1116 if (intervalMillis > minIntervalMillis) { 1117 mAtoms.sipTransportFeatureTagStatsPullTimestampMillis = getWallTimeMillis(); 1118 SipTransportFeatureTagStats[] previousStats = mAtoms.sipTransportFeatureTagStats; 1119 1120 for (SipTransportFeatureTagStats stat: previousStats) { 1121 // in case pull interval is greater than 24H, normalize it as of one day interval 1122 if (intervalMillis > DAY_IN_MILLIS) { 1123 stat.associatedMillis = normalizeDurationTo24H(stat.associatedMillis, 1124 intervalMillis); 1125 } 1126 } 1127 1128 mAtoms.sipTransportFeatureTagStats = new SipTransportFeatureTagStats[0]; 1129 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1130 return previousStats; 1131 } else { 1132 return null; 1133 } 1134 } 1135 1136 /** 1137 * Returns and clears the SipMessageResponse if last pulled longer than {@code 1138 * minIntervalMillis} ago, otherwise returns {@code null}. 1139 */ 1140 @Nullable getSipMessageResponse(long minIntervalMillis)1141 public synchronized SipMessageResponse[] getSipMessageResponse(long minIntervalMillis) { 1142 if (getWallTimeMillis() - mAtoms.sipMessageResponsePullTimestampMillis 1143 > minIntervalMillis) { 1144 mAtoms.sipMessageResponsePullTimestampMillis = getWallTimeMillis(); 1145 SipMessageResponse[] previousStats = 1146 mAtoms.sipMessageResponse; 1147 mAtoms.sipMessageResponse = new SipMessageResponse[0]; 1148 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1149 return previousStats; 1150 } else { 1151 return null; 1152 } 1153 } 1154 1155 /** 1156 * Returns and clears the SipTransportSession if last pulled longer than {@code 1157 * minIntervalMillis} ago, otherwise returns {@code null}. 1158 */ 1159 @Nullable getSipTransportSession(long minIntervalMillis)1160 public synchronized SipTransportSession[] getSipTransportSession(long minIntervalMillis) { 1161 if (getWallTimeMillis() - mAtoms.sipTransportSessionPullTimestampMillis 1162 > minIntervalMillis) { 1163 mAtoms.sipTransportSessionPullTimestampMillis = getWallTimeMillis(); 1164 SipTransportSession[] previousStats = 1165 mAtoms.sipTransportSession; 1166 mAtoms.sipTransportSession = new SipTransportSession[0]; 1167 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1168 return previousStats; 1169 } else { 1170 return null; 1171 } 1172 } 1173 1174 /** 1175 * Returns and clears the ImsDedicatedBearerListenerEvent if last pulled longer than {@code 1176 * minIntervalMillis} ago, otherwise returns {@code null}. 1177 */ 1178 @Nullable getImsDedicatedBearerListenerEvent( long minIntervalMillis)1179 public synchronized ImsDedicatedBearerListenerEvent[] getImsDedicatedBearerListenerEvent( 1180 long minIntervalMillis) { 1181 if (getWallTimeMillis() - mAtoms.imsDedicatedBearerListenerEventPullTimestampMillis 1182 > minIntervalMillis) { 1183 mAtoms.imsDedicatedBearerListenerEventPullTimestampMillis = getWallTimeMillis(); 1184 ImsDedicatedBearerListenerEvent[] previousStats = 1185 mAtoms.imsDedicatedBearerListenerEvent; 1186 mAtoms.imsDedicatedBearerListenerEvent = new ImsDedicatedBearerListenerEvent[0]; 1187 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1188 return previousStats; 1189 } else { 1190 return null; 1191 } 1192 } 1193 1194 /** 1195 * Returns and clears the ImsDedicatedBearerEvent if last pulled longer than {@code 1196 * minIntervalMillis} ago, otherwise returns {@code null}. 1197 */ 1198 @Nullable getImsDedicatedBearerEvent( long minIntervalMillis)1199 public synchronized ImsDedicatedBearerEvent[] getImsDedicatedBearerEvent( 1200 long minIntervalMillis) { 1201 if (getWallTimeMillis() - mAtoms.imsDedicatedBearerEventPullTimestampMillis 1202 > minIntervalMillis) { 1203 mAtoms.imsDedicatedBearerEventPullTimestampMillis = getWallTimeMillis(); 1204 ImsDedicatedBearerEvent[] previousStats = 1205 mAtoms.imsDedicatedBearerEvent; 1206 mAtoms.imsDedicatedBearerEvent = new ImsDedicatedBearerEvent[0]; 1207 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1208 return previousStats; 1209 } else { 1210 return null; 1211 } 1212 } 1213 1214 /** 1215 * Returns and clears the ImsRegistrationServiceDescStats if last pulled longer than {@code 1216 * minIntervalMillis} ago, otherwise returns {@code null}. 1217 */ 1218 @Nullable getImsRegistrationServiceDescStats(long minIntervalMillis)1219 public synchronized ImsRegistrationServiceDescStats[] getImsRegistrationServiceDescStats(long 1220 minIntervalMillis) { 1221 long intervalMillis = 1222 getWallTimeMillis() - mAtoms.imsRegistrationServiceDescStatsPullTimestampMillis; 1223 if (intervalMillis > minIntervalMillis) { 1224 mAtoms.imsRegistrationServiceDescStatsPullTimestampMillis = getWallTimeMillis(); 1225 ImsRegistrationServiceDescStats[] previousStats = 1226 mAtoms.imsRegistrationServiceDescStats; 1227 1228 for (ImsRegistrationServiceDescStats stat: previousStats) { 1229 // in case pull interval is greater than 24H, normalize it as of one day interval 1230 if (intervalMillis > DAY_IN_MILLIS) { 1231 stat.publishedMillis = normalizeDurationTo24H(stat.publishedMillis, 1232 intervalMillis); 1233 } 1234 } 1235 1236 mAtoms.imsRegistrationServiceDescStats = new ImsRegistrationServiceDescStats[0]; 1237 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1238 return previousStats; 1239 } else { 1240 return null; 1241 } 1242 } 1243 1244 /** 1245 * Returns and clears the UceEventStats if last pulled longer than {@code 1246 * minIntervalMillis} ago, otherwise returns {@code null}. 1247 */ 1248 @Nullable getUceEventStats(long minIntervalMillis)1249 public synchronized UceEventStats[] getUceEventStats(long minIntervalMillis) { 1250 if (getWallTimeMillis() - mAtoms.uceEventStatsPullTimestampMillis > minIntervalMillis) { 1251 mAtoms.uceEventStatsPullTimestampMillis = getWallTimeMillis(); 1252 UceEventStats[] previousStats = mAtoms.uceEventStats; 1253 mAtoms.uceEventStats = new UceEventStats[0]; 1254 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1255 return previousStats; 1256 } else { 1257 return null; 1258 } 1259 } 1260 1261 /** 1262 * Returns and clears the PresenceNotifyEvent if last pulled longer than {@code 1263 * minIntervalMillis} ago, otherwise returns {@code null}. 1264 */ 1265 @Nullable getPresenceNotifyEvent(long minIntervalMillis)1266 public synchronized PresenceNotifyEvent[] getPresenceNotifyEvent(long minIntervalMillis) { 1267 if (getWallTimeMillis() - mAtoms.presenceNotifyEventPullTimestampMillis 1268 > minIntervalMillis) { 1269 mAtoms.presenceNotifyEventPullTimestampMillis = getWallTimeMillis(); 1270 PresenceNotifyEvent[] previousStats = mAtoms.presenceNotifyEvent; 1271 mAtoms.presenceNotifyEvent = new PresenceNotifyEvent[0]; 1272 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1273 return previousStats; 1274 } else { 1275 return null; 1276 } 1277 } 1278 1279 /** 1280 * Returns and clears the GbaEvent if last pulled longer than {@code 1281 * minIntervalMillis} ago, otherwise returns {@code null}. 1282 */ 1283 @Nullable getGbaEvent(long minIntervalMillis)1284 public synchronized GbaEvent[] getGbaEvent(long minIntervalMillis) { 1285 if (getWallTimeMillis() - mAtoms.gbaEventPullTimestampMillis > minIntervalMillis) { 1286 mAtoms.gbaEventPullTimestampMillis = getWallTimeMillis(); 1287 GbaEvent[] previousStats = mAtoms.gbaEvent; 1288 mAtoms.gbaEvent = new GbaEvent[0]; 1289 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1290 return previousStats; 1291 } else { 1292 return null; 1293 } 1294 } 1295 1296 /** 1297 * Returns the unmetered networks bitmask for a given phone id. Returns 0 if there is 1298 * no existing UnmeteredNetworks for the given phone id or the carrier id doesn't match. 1299 * Existing UnmeteredNetworks is discarded after. 1300 */ getUnmeteredNetworks(int phoneId, int carrierId)1301 public synchronized @NetworkTypeBitMask long getUnmeteredNetworks(int phoneId, int carrierId) { 1302 UnmeteredNetworks existingStats = findUnmeteredNetworks(phoneId); 1303 if (existingStats == null) { 1304 return 0L; 1305 } 1306 @NetworkTypeBitMask 1307 long bitmask = 1308 existingStats.carrierId != carrierId ? 0L : existingStats.unmeteredNetworksBitmask; 1309 mAtoms.unmeteredNetworks = 1310 sanitizeAtoms( 1311 ArrayUtils.removeElement( 1312 UnmeteredNetworks.class, 1313 mAtoms.unmeteredNetworks, 1314 existingStats), 1315 UnmeteredNetworks.class); 1316 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1317 return bitmask; 1318 } 1319 1320 /** 1321 * Returns and clears the OutgoingShortCodeSms if last pulled longer than {@code 1322 * minIntervalMillis} ago, otherwise returns {@code null}. 1323 */ 1324 @Nullable getOutgoingShortCodeSms(long minIntervalMillis)1325 public synchronized OutgoingShortCodeSms[] getOutgoingShortCodeSms(long minIntervalMillis) { 1326 if ((getWallTimeMillis() - mAtoms.outgoingShortCodeSmsPullTimestampMillis) 1327 > minIntervalMillis) { 1328 mAtoms.outgoingShortCodeSmsPullTimestampMillis = getWallTimeMillis(); 1329 OutgoingShortCodeSms[] previousOutgoingShortCodeSms = mAtoms.outgoingShortCodeSms; 1330 mAtoms.outgoingShortCodeSms = new OutgoingShortCodeSms[0]; 1331 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1332 return previousOutgoingShortCodeSms; 1333 } else { 1334 return null; 1335 } 1336 } 1337 1338 /** 1339 * Returns and clears the {@link SatelliteController} stats if last pulled longer than {@code 1340 * minIntervalMillis} ago, otherwise returns {@code null}. 1341 */ 1342 @Nullable getSatelliteControllerStats(long minIntervalMillis)1343 public synchronized SatelliteController[] getSatelliteControllerStats(long minIntervalMillis) { 1344 if (getWallTimeMillis() - mAtoms.satelliteControllerPullTimestampMillis 1345 > minIntervalMillis) { 1346 mAtoms.satelliteControllerPullTimestampMillis = getWallTimeMillis(); 1347 SatelliteController[] statsArray = mAtoms.satelliteController; 1348 mAtoms.satelliteController = new SatelliteController[0]; 1349 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1350 return statsArray; 1351 } else { 1352 return null; 1353 } 1354 } 1355 1356 /** 1357 * Returns and clears the {@link SatelliteSession} stats if last pulled longer than {@code 1358 * minIntervalMillis} ago, otherwise returns {@code null}. 1359 */ 1360 @Nullable getSatelliteSessionStats(long minIntervalMillis)1361 public synchronized SatelliteSession[] getSatelliteSessionStats(long minIntervalMillis) { 1362 if (getWallTimeMillis() - mAtoms.satelliteSessionPullTimestampMillis 1363 > minIntervalMillis) { 1364 mAtoms.satelliteSessionPullTimestampMillis = getWallTimeMillis(); 1365 SatelliteSession[] statsArray = mAtoms.satelliteSession; 1366 mAtoms.satelliteSession = new SatelliteSession[0]; 1367 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1368 return statsArray; 1369 } else { 1370 return null; 1371 } 1372 } 1373 1374 /** 1375 * Returns and clears the {@link SatelliteIncomingDatagram} stats if last pulled longer than 1376 * {@code minIntervalMillis} ago, otherwise returns {@code null}. 1377 */ 1378 @Nullable getSatelliteIncomingDatagramStats( long minIntervalMillis)1379 public synchronized SatelliteIncomingDatagram[] getSatelliteIncomingDatagramStats( 1380 long minIntervalMillis) { 1381 if (getWallTimeMillis() - mAtoms.satelliteIncomingDatagramPullTimestampMillis 1382 > minIntervalMillis) { 1383 mAtoms.satelliteIncomingDatagramPullTimestampMillis = getWallTimeMillis(); 1384 SatelliteIncomingDatagram[] statsArray = mAtoms.satelliteIncomingDatagram; 1385 mAtoms.satelliteIncomingDatagram = new SatelliteIncomingDatagram[0]; 1386 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1387 return statsArray; 1388 } else { 1389 return null; 1390 } 1391 } 1392 1393 /** 1394 * Returns and clears the {@link SatelliteOutgoingDatagram} stats if last pulled longer than 1395 * {@code minIntervalMillis} ago, otherwise returns {@code null}. 1396 */ 1397 @Nullable getSatelliteOutgoingDatagramStats( long minIntervalMillis)1398 public synchronized SatelliteOutgoingDatagram[] getSatelliteOutgoingDatagramStats( 1399 long minIntervalMillis) { 1400 if (getWallTimeMillis() - mAtoms.satelliteOutgoingDatagramPullTimestampMillis 1401 > minIntervalMillis) { 1402 mAtoms.satelliteOutgoingDatagramPullTimestampMillis = getWallTimeMillis(); 1403 SatelliteOutgoingDatagram[] statsArray = mAtoms.satelliteOutgoingDatagram; 1404 mAtoms.satelliteOutgoingDatagram = new SatelliteOutgoingDatagram[0]; 1405 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1406 return statsArray; 1407 } else { 1408 return null; 1409 } 1410 } 1411 1412 /** 1413 * Returns and clears the {@link SatelliteProvision} stats if last pulled longer than {@code 1414 * minIntervalMillis} ago, otherwise returns {@code null}. 1415 */ 1416 @Nullable getSatelliteProvisionStats(long minIntervalMillis)1417 public synchronized SatelliteProvision[] getSatelliteProvisionStats(long minIntervalMillis) { 1418 if (getWallTimeMillis() - mAtoms.satelliteProvisionPullTimestampMillis 1419 > minIntervalMillis) { 1420 mAtoms.satelliteProvisionPullTimestampMillis = getWallTimeMillis(); 1421 SatelliteProvision[] statsArray = mAtoms.satelliteProvision; 1422 mAtoms.satelliteProvision = new SatelliteProvision[0]; 1423 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1424 return statsArray; 1425 } else { 1426 return null; 1427 } 1428 } 1429 1430 /** 1431 * Returns and clears the {@link SatelliteSosMessageRecommender} stats if last pulled longer 1432 * than {@code minIntervalMillis} ago, otherwise returns {@code null}. 1433 */ 1434 @Nullable getSatelliteSosMessageRecommenderStats( long minIntervalMillis)1435 public synchronized SatelliteSosMessageRecommender[] getSatelliteSosMessageRecommenderStats( 1436 long minIntervalMillis) { 1437 if (getWallTimeMillis() - mAtoms.satelliteSosMessageRecommenderPullTimestampMillis 1438 > minIntervalMillis) { 1439 mAtoms.satelliteProvisionPullTimestampMillis = getWallTimeMillis(); 1440 SatelliteSosMessageRecommender[] statsArray = mAtoms.satelliteSosMessageRecommender; 1441 mAtoms.satelliteSosMessageRecommender = new SatelliteSosMessageRecommender[0]; 1442 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 1443 return statsArray; 1444 } else { 1445 return null; 1446 } 1447 } 1448 1449 /** Saves {@link PersistAtoms} to a file in private storage immediately. */ flushAtoms()1450 public synchronized void flushAtoms() { 1451 saveAtomsToFile(0); 1452 } 1453 1454 /** Clears atoms for testing purpose. */ clearAtoms()1455 public synchronized void clearAtoms() { 1456 mAtoms = makeNewPersistAtoms(); 1457 saveAtomsToFile(0); 1458 } 1459 1460 /** Loads {@link PersistAtoms} from a file in private storage. */ loadAtomsFromFile()1461 private PersistAtoms loadAtomsFromFile() { 1462 try { 1463 PersistAtoms atoms = 1464 PersistAtoms.parseFrom( 1465 Files.readAllBytes(mContext.getFileStreamPath(FILENAME).toPath())); 1466 // Start from scratch if build changes, since mixing atoms from different builds could 1467 // produce strange results 1468 if (!Build.FINGERPRINT.equals(atoms.buildFingerprint)) { 1469 Rlog.d(TAG, "Build changed"); 1470 return makeNewPersistAtoms(); 1471 } 1472 // check all the fields in case of situations such as OTA or crash during saving 1473 atoms.voiceCallRatUsage = 1474 sanitizeAtoms(atoms.voiceCallRatUsage, VoiceCallRatUsage.class); 1475 atoms.voiceCallSession = 1476 sanitizeAtoms( 1477 atoms.voiceCallSession, 1478 VoiceCallSession.class, 1479 mMaxNumVoiceCallSessions); 1480 atoms.incomingSms = sanitizeAtoms(atoms.incomingSms, IncomingSms.class, mMaxNumSms); 1481 atoms.outgoingSms = sanitizeAtoms(atoms.outgoingSms, OutgoingSms.class, mMaxNumSms); 1482 atoms.carrierIdMismatch = 1483 sanitizeAtoms( 1484 atoms.carrierIdMismatch, 1485 CarrierIdMismatch.class, 1486 mMaxNumCarrierIdMismatches); 1487 atoms.dataCallSession = 1488 sanitizeAtoms( 1489 atoms.dataCallSession, 1490 DataCallSession.class, 1491 mMaxNumDataCallSessions); 1492 atoms.cellularServiceState = 1493 sanitizeAtoms( 1494 atoms.cellularServiceState, 1495 CellularServiceState.class, 1496 mMaxNumCellularServiceStates); 1497 atoms.cellularDataServiceSwitch = 1498 sanitizeAtoms( 1499 atoms.cellularDataServiceSwitch, 1500 CellularDataServiceSwitch.class, 1501 mMaxNumCellularDataSwitches); 1502 atoms.imsRegistrationStats = 1503 sanitizeAtoms( 1504 atoms.imsRegistrationStats, 1505 ImsRegistrationStats.class, 1506 mMaxNumImsRegistrationStats); 1507 atoms.imsRegistrationTermination = 1508 sanitizeAtoms( 1509 atoms.imsRegistrationTermination, 1510 ImsRegistrationTermination.class, 1511 mMaxNumImsRegistrationTerminations); 1512 atoms.networkRequestsV2 = 1513 sanitizeAtoms(atoms.networkRequestsV2, NetworkRequestsV2.class); 1514 atoms.imsRegistrationFeatureTagStats = 1515 sanitizeAtoms( 1516 atoms.imsRegistrationFeatureTagStats, 1517 ImsRegistrationFeatureTagStats.class, 1518 mMaxNumImsRegistrationFeatureStats); 1519 atoms.rcsClientProvisioningStats = 1520 sanitizeAtoms( 1521 atoms.rcsClientProvisioningStats, 1522 RcsClientProvisioningStats.class, 1523 mMaxNumRcsClientProvisioningStats); 1524 atoms.rcsAcsProvisioningStats = 1525 sanitizeAtoms( 1526 atoms.rcsAcsProvisioningStats, 1527 RcsAcsProvisioningStats.class, 1528 mMaxNumRcsAcsProvisioningStats); 1529 atoms.sipDelegateStats = 1530 sanitizeAtoms( 1531 atoms.sipDelegateStats, 1532 SipDelegateStats.class, 1533 mMaxNumSipDelegateStats); 1534 atoms.sipTransportFeatureTagStats = 1535 sanitizeAtoms( 1536 atoms.sipTransportFeatureTagStats, 1537 SipTransportFeatureTagStats.class, 1538 mMaxNumSipTransportFeatureTagStats); 1539 atoms.sipMessageResponse = 1540 sanitizeAtoms( 1541 atoms.sipMessageResponse, 1542 SipMessageResponse.class, 1543 mMaxNumSipMessageResponseStats); 1544 atoms.sipTransportSession = 1545 sanitizeAtoms( 1546 atoms.sipTransportSession, 1547 SipTransportSession.class, 1548 mMaxNumSipTransportSessionStats); 1549 atoms.imsDedicatedBearerListenerEvent = 1550 sanitizeAtoms( 1551 atoms.imsDedicatedBearerListenerEvent, 1552 ImsDedicatedBearerListenerEvent.class, 1553 mMaxNumDedicatedBearerListenerEventStats); 1554 atoms.imsDedicatedBearerEvent = 1555 sanitizeAtoms( 1556 atoms.imsDedicatedBearerEvent, 1557 ImsDedicatedBearerEvent.class, 1558 mMaxNumDedicatedBearerEventStats); 1559 atoms.imsRegistrationServiceDescStats = 1560 sanitizeAtoms( 1561 atoms.imsRegistrationServiceDescStats, 1562 ImsRegistrationServiceDescStats.class, 1563 mMaxNumImsRegistrationServiceDescStats); 1564 atoms.uceEventStats = 1565 sanitizeAtoms( 1566 atoms.uceEventStats, 1567 UceEventStats.class, 1568 mMaxNumUceEventStats); 1569 atoms.presenceNotifyEvent = 1570 sanitizeAtoms( 1571 atoms.presenceNotifyEvent, 1572 PresenceNotifyEvent.class, 1573 mMaxNumPresenceNotifyEventStats); 1574 atoms.gbaEvent = 1575 sanitizeAtoms( 1576 atoms.gbaEvent, 1577 GbaEvent.class, 1578 mMaxNumGbaEventStats); 1579 atoms.unmeteredNetworks = 1580 sanitizeAtoms( 1581 atoms.unmeteredNetworks, 1582 UnmeteredNetworks.class 1583 ); 1584 atoms.outgoingShortCodeSms = sanitizeAtoms(atoms.outgoingShortCodeSms, 1585 OutgoingShortCodeSms.class, mMaxOutgoingShortCodeSms); 1586 atoms.satelliteController = sanitizeAtoms(atoms.satelliteController, 1587 SatelliteController.class, mMaxNumSatelliteControllerStats); 1588 atoms.satelliteSession = sanitizeAtoms(atoms.satelliteSession, 1589 SatelliteSession.class, mMaxNumSatelliteStats); 1590 atoms.satelliteIncomingDatagram = sanitizeAtoms(atoms.satelliteIncomingDatagram, 1591 SatelliteIncomingDatagram.class, mMaxNumSatelliteStats); 1592 atoms.satelliteOutgoingDatagram = sanitizeAtoms(atoms.satelliteOutgoingDatagram, 1593 SatelliteOutgoingDatagram.class, mMaxNumSatelliteStats); 1594 atoms.satelliteProvision = sanitizeAtoms(atoms.satelliteProvision, 1595 SatelliteProvision.class, mMaxNumSatelliteStats); 1596 atoms.satelliteSosMessageRecommender = sanitizeAtoms( 1597 atoms.satelliteSosMessageRecommender, SatelliteSosMessageRecommender.class, 1598 mMaxNumSatelliteStats); 1599 1600 // out of caution, sanitize also the timestamps 1601 atoms.voiceCallRatUsagePullTimestampMillis = 1602 sanitizeTimestamp(atoms.voiceCallRatUsagePullTimestampMillis); 1603 atoms.voiceCallSessionPullTimestampMillis = 1604 sanitizeTimestamp(atoms.voiceCallSessionPullTimestampMillis); 1605 atoms.incomingSmsPullTimestampMillis = 1606 sanitizeTimestamp(atoms.incomingSmsPullTimestampMillis); 1607 atoms.outgoingSmsPullTimestampMillis = 1608 sanitizeTimestamp(atoms.outgoingSmsPullTimestampMillis); 1609 atoms.dataCallSessionPullTimestampMillis = 1610 sanitizeTimestamp(atoms.dataCallSessionPullTimestampMillis); 1611 atoms.cellularServiceStatePullTimestampMillis = 1612 sanitizeTimestamp(atoms.cellularServiceStatePullTimestampMillis); 1613 atoms.cellularDataServiceSwitchPullTimestampMillis = 1614 sanitizeTimestamp(atoms.cellularDataServiceSwitchPullTimestampMillis); 1615 atoms.imsRegistrationStatsPullTimestampMillis = 1616 sanitizeTimestamp(atoms.imsRegistrationStatsPullTimestampMillis); 1617 atoms.imsRegistrationTerminationPullTimestampMillis = 1618 sanitizeTimestamp(atoms.imsRegistrationTerminationPullTimestampMillis); 1619 atoms.networkRequestsV2PullTimestampMillis = 1620 sanitizeTimestamp(atoms.networkRequestsV2PullTimestampMillis); 1621 atoms.imsRegistrationFeatureTagStatsPullTimestampMillis = 1622 sanitizeTimestamp(atoms.imsRegistrationFeatureTagStatsPullTimestampMillis); 1623 atoms.rcsClientProvisioningStatsPullTimestampMillis = 1624 sanitizeTimestamp(atoms.rcsClientProvisioningStatsPullTimestampMillis); 1625 atoms.rcsAcsProvisioningStatsPullTimestampMillis = 1626 sanitizeTimestamp(atoms.rcsAcsProvisioningStatsPullTimestampMillis); 1627 atoms.sipDelegateStatsPullTimestampMillis = 1628 sanitizeTimestamp(atoms.sipDelegateStatsPullTimestampMillis); 1629 atoms.sipTransportFeatureTagStatsPullTimestampMillis = 1630 sanitizeTimestamp(atoms.sipTransportFeatureTagStatsPullTimestampMillis); 1631 atoms.sipMessageResponsePullTimestampMillis = 1632 sanitizeTimestamp(atoms.sipMessageResponsePullTimestampMillis); 1633 atoms.sipTransportSessionPullTimestampMillis = 1634 sanitizeTimestamp(atoms.sipTransportSessionPullTimestampMillis); 1635 atoms.imsDedicatedBearerListenerEventPullTimestampMillis = 1636 sanitizeTimestamp(atoms.imsDedicatedBearerListenerEventPullTimestampMillis); 1637 atoms.imsDedicatedBearerEventPullTimestampMillis = 1638 sanitizeTimestamp(atoms.imsDedicatedBearerEventPullTimestampMillis); 1639 atoms.imsRegistrationServiceDescStatsPullTimestampMillis = 1640 sanitizeTimestamp(atoms.imsRegistrationServiceDescStatsPullTimestampMillis); 1641 atoms.uceEventStatsPullTimestampMillis = 1642 sanitizeTimestamp(atoms.uceEventStatsPullTimestampMillis); 1643 atoms.presenceNotifyEventPullTimestampMillis = 1644 sanitizeTimestamp(atoms.presenceNotifyEventPullTimestampMillis); 1645 atoms.gbaEventPullTimestampMillis = 1646 sanitizeTimestamp(atoms.gbaEventPullTimestampMillis); 1647 atoms.outgoingShortCodeSmsPullTimestampMillis = 1648 sanitizeTimestamp(atoms.outgoingShortCodeSmsPullTimestampMillis); 1649 atoms.satelliteControllerPullTimestampMillis = 1650 sanitizeTimestamp(atoms.satelliteControllerPullTimestampMillis); 1651 atoms.satelliteSessionPullTimestampMillis = 1652 sanitizeTimestamp(atoms.satelliteSessionPullTimestampMillis); 1653 atoms.satelliteIncomingDatagramPullTimestampMillis = 1654 sanitizeTimestamp(atoms.satelliteIncomingDatagramPullTimestampMillis); 1655 atoms.satelliteOutgoingDatagramPullTimestampMillis = 1656 sanitizeTimestamp(atoms.satelliteOutgoingDatagramPullTimestampMillis); 1657 atoms.satelliteProvisionPullTimestampMillis = 1658 sanitizeTimestamp(atoms.satelliteProvisionPullTimestampMillis); 1659 atoms.satelliteSosMessageRecommenderPullTimestampMillis = 1660 sanitizeTimestamp(atoms.satelliteSosMessageRecommenderPullTimestampMillis); 1661 return atoms; 1662 } catch (NoSuchFileException e) { 1663 Rlog.d(TAG, "PersistAtoms file not found"); 1664 } catch (IOException | NullPointerException e) { 1665 Rlog.e(TAG, "cannot load/parse PersistAtoms", e); 1666 } 1667 return makeNewPersistAtoms(); 1668 } 1669 1670 /** 1671 * Posts message to save a copy of {@link PersistAtoms} to a file after a delay or immediately. 1672 * 1673 * <p>The delay is introduced to avoid too frequent operations to disk, which would negatively 1674 * impact the power consumption. 1675 */ saveAtomsToFile(int delayMillis)1676 private synchronized void saveAtomsToFile(int delayMillis) { 1677 mHandler.removeCallbacks(mSaveRunnable); 1678 if (delayMillis > 0 && !mSaveImmediately) { 1679 if (mHandler.postDelayed(mSaveRunnable, delayMillis)) { 1680 return; 1681 } 1682 } 1683 // In case of error posting the event or if delay is 0, save immediately 1684 saveAtomsToFileNow(); 1685 } 1686 1687 /** Saves a copy of {@link PersistAtoms} to a file in private storage. */ saveAtomsToFileNow()1688 private synchronized void saveAtomsToFileNow() { 1689 try (FileOutputStream stream = mContext.openFileOutput(FILENAME, Context.MODE_PRIVATE)) { 1690 stream.write(PersistAtoms.toByteArray(mAtoms)); 1691 } catch (IOException e) { 1692 Rlog.e(TAG, "cannot save PersistAtoms", e); 1693 } 1694 } 1695 1696 /** 1697 * Returns the service state that has the same dimension values with the given one, or {@code 1698 * null} if it does not exist. 1699 */ find(CellularServiceState key)1700 private @Nullable CellularServiceState find(CellularServiceState key) { 1701 for (CellularServiceState state : mAtoms.cellularServiceState) { 1702 if (state.voiceRat == key.voiceRat 1703 && state.dataRat == key.dataRat 1704 && state.voiceRoamingType == key.voiceRoamingType 1705 && state.dataRoamingType == key.dataRoamingType 1706 && state.isEndc == key.isEndc 1707 && state.simSlotIndex == key.simSlotIndex 1708 && state.isMultiSim == key.isMultiSim 1709 && state.carrierId == key.carrierId 1710 && state.isEmergencyOnly == key.isEmergencyOnly 1711 && state.isInternetPdnUp == key.isInternetPdnUp 1712 && state.foldState == key.foldState 1713 && state.overrideVoiceService == key.overrideVoiceService 1714 && state.isDataEnabled == key.isDataEnabled) { 1715 return state; 1716 } 1717 } 1718 return null; 1719 } 1720 1721 /** 1722 * Returns the data service switch that has the same dimension values with the given one, or 1723 * {@code null} if it does not exist. 1724 */ find(CellularDataServiceSwitch key)1725 private @Nullable CellularDataServiceSwitch find(CellularDataServiceSwitch key) { 1726 for (CellularDataServiceSwitch serviceSwitch : mAtoms.cellularDataServiceSwitch) { 1727 if (serviceSwitch.ratFrom == key.ratFrom 1728 && serviceSwitch.ratTo == key.ratTo 1729 && serviceSwitch.simSlotIndex == key.simSlotIndex 1730 && serviceSwitch.isMultiSim == key.isMultiSim 1731 && serviceSwitch.carrierId == key.carrierId) { 1732 return serviceSwitch; 1733 } 1734 } 1735 return null; 1736 } 1737 1738 /** 1739 * Returns the carrier ID mismatch event that has the same dimension values with the given one, 1740 * or {@code null} if it does not exist. 1741 */ find(CarrierIdMismatch key)1742 private @Nullable CarrierIdMismatch find(CarrierIdMismatch key) { 1743 for (CarrierIdMismatch mismatch : mAtoms.carrierIdMismatch) { 1744 if (mismatch.mccMnc.equals(key.mccMnc) 1745 && mismatch.gid1.equals(key.gid1) 1746 && mismatch.spn.equals(key.spn) 1747 && mismatch.pnn.equals(key.pnn)) { 1748 return mismatch; 1749 } 1750 } 1751 return null; 1752 } 1753 1754 /** 1755 * Returns the IMS registration stats that has the same dimension values with the given one, or 1756 * {@code null} if it does not exist. 1757 */ find(ImsRegistrationStats key)1758 private @Nullable ImsRegistrationStats find(ImsRegistrationStats key) { 1759 for (ImsRegistrationStats stats : mAtoms.imsRegistrationStats) { 1760 if (stats.carrierId == key.carrierId 1761 && stats.simSlotIndex == key.simSlotIndex 1762 && stats.rat == key.rat) { 1763 return stats; 1764 } 1765 } 1766 return null; 1767 } 1768 1769 /** 1770 * Returns the IMS registration termination that has the same dimension values with the given 1771 * one, or {@code null} if it does not exist. 1772 */ find(ImsRegistrationTermination key)1773 private @Nullable ImsRegistrationTermination find(ImsRegistrationTermination key) { 1774 for (ImsRegistrationTermination termination : mAtoms.imsRegistrationTermination) { 1775 if (termination.carrierId == key.carrierId 1776 && termination.isMultiSim == key.isMultiSim 1777 && termination.ratAtEnd == key.ratAtEnd 1778 && termination.setupFailed == key.setupFailed 1779 && termination.reasonCode == key.reasonCode 1780 && termination.extraCode == key.extraCode 1781 && termination.extraMessage.equals(key.extraMessage)) { 1782 return termination; 1783 } 1784 } 1785 return null; 1786 } 1787 1788 /** 1789 * Returns the network requests event that has the same carrier id and capability as the given 1790 * one, or {@code null} if it does not exist. 1791 */ find(NetworkRequestsV2 key)1792 private @Nullable NetworkRequestsV2 find(NetworkRequestsV2 key) { 1793 for (NetworkRequestsV2 item : mAtoms.networkRequestsV2) { 1794 if (item.carrierId == key.carrierId && item.capability == key.capability) { 1795 return item; 1796 } 1797 } 1798 return null; 1799 } 1800 1801 /** 1802 * Returns the index of data call session that has the same random dimension as the given one, 1803 * or -1 if it does not exist. 1804 */ findIndex(DataCallSession key)1805 private int findIndex(DataCallSession key) { 1806 for (int i = 0; i < mAtoms.dataCallSession.length; i++) { 1807 if (mAtoms.dataCallSession[i].dimension == key.dimension) { 1808 return i; 1809 } 1810 } 1811 return -1; 1812 } 1813 /** 1814 * Returns the Dedicated Bearer Listener event that has the same carrier id, slot id, rat, qci 1815 * and established state as the given one, or {@code null} if it does not exist. 1816 */ find(ImsDedicatedBearerListenerEvent key)1817 private @Nullable ImsDedicatedBearerListenerEvent find(ImsDedicatedBearerListenerEvent key) { 1818 for (ImsDedicatedBearerListenerEvent stats : mAtoms.imsDedicatedBearerListenerEvent) { 1819 if (stats.carrierId == key.carrierId 1820 && stats.slotId == key.slotId 1821 && stats.ratAtEnd == key.ratAtEnd 1822 && stats.qci == key.qci 1823 && stats.dedicatedBearerEstablished == key.dedicatedBearerEstablished) { 1824 return stats; 1825 } 1826 } 1827 return null; 1828 } 1829 1830 /** 1831 * Returns the Dedicated Bearer event that has the same carrier id, slot id, rat, 1832 * qci, bearer state, local/remote connection and exsting listener as the given one, 1833 * or {@code null} if it does not exist. 1834 */ find(ImsDedicatedBearerEvent key)1835 private @Nullable ImsDedicatedBearerEvent find(ImsDedicatedBearerEvent key) { 1836 for (ImsDedicatedBearerEvent stats : mAtoms.imsDedicatedBearerEvent) { 1837 if (stats.carrierId == key.carrierId 1838 && stats.slotId == key.slotId 1839 && stats.ratAtEnd == key.ratAtEnd 1840 && stats.qci == key.qci 1841 && stats.bearerState == key.bearerState 1842 && stats.localConnectionInfoReceived == key.localConnectionInfoReceived 1843 && stats.remoteConnectionInfoReceived == key.remoteConnectionInfoReceived 1844 && stats.hasListeners == key.hasListeners) { 1845 return stats; 1846 } 1847 } 1848 return null; 1849 } 1850 1851 /** 1852 * Returns the Registration Feature Tag that has the same carrier id, slot id, 1853 * feature tag name or custom feature tag name and registration tech as the given one, 1854 * or {@code null} if it does not exist. 1855 */ find(ImsRegistrationFeatureTagStats key)1856 private @Nullable ImsRegistrationFeatureTagStats find(ImsRegistrationFeatureTagStats key) { 1857 for (ImsRegistrationFeatureTagStats stats : mAtoms.imsRegistrationFeatureTagStats) { 1858 if (stats.carrierId == key.carrierId 1859 && stats.slotId == key.slotId 1860 && stats.featureTagName == key.featureTagName 1861 && stats.registrationTech == key.registrationTech) { 1862 return stats; 1863 } 1864 } 1865 return null; 1866 } 1867 1868 /** 1869 * Returns Client Provisioning that has the same carrier id, slot id and event as the given 1870 * one, or {@code null} if it does not exist. 1871 */ find(RcsClientProvisioningStats key)1872 private @Nullable RcsClientProvisioningStats find(RcsClientProvisioningStats key) { 1873 for (RcsClientProvisioningStats stats : mAtoms.rcsClientProvisioningStats) { 1874 if (stats.carrierId == key.carrierId 1875 && stats.slotId == key.slotId 1876 && stats.event == key.event) { 1877 return stats; 1878 } 1879 } 1880 return null; 1881 } 1882 1883 /** 1884 * Returns ACS Provisioning that has the same carrier id, slot id, response code, response type 1885 * and SR supported as the given one, or {@code null} if it does not exist. 1886 */ find(RcsAcsProvisioningStats key)1887 private @Nullable RcsAcsProvisioningStats find(RcsAcsProvisioningStats key) { 1888 for (RcsAcsProvisioningStats stats : mAtoms.rcsAcsProvisioningStats) { 1889 if (stats.carrierId == key.carrierId 1890 && stats.slotId == key.slotId 1891 && stats.responseCode == key.responseCode 1892 && stats.responseType == key.responseType 1893 && stats.isSingleRegistrationEnabled == key.isSingleRegistrationEnabled) { 1894 return stats; 1895 } 1896 } 1897 return null; 1898 } 1899 1900 /** 1901 * Returns Sip Message Response that has the same carrier id, slot id, method, response, 1902 * direction and error as the given one, or {@code null} if it does not exist. 1903 */ find(SipMessageResponse key)1904 private @Nullable SipMessageResponse find(SipMessageResponse key) { 1905 for (SipMessageResponse stats : mAtoms.sipMessageResponse) { 1906 if (stats.carrierId == key.carrierId 1907 && stats.slotId == key.slotId 1908 && stats.sipMessageMethod == key.sipMessageMethod 1909 && stats.sipMessageResponse == key.sipMessageResponse 1910 && stats.sipMessageDirection == key.sipMessageDirection 1911 && stats.messageError == key.messageError) { 1912 return stats; 1913 } 1914 } 1915 return null; 1916 } 1917 1918 /** 1919 * Returns Sip Transport Session that has the same carrier id, slot id, method, direction and 1920 * response as the given one, or {@code null} if it does not exist. 1921 */ find(SipTransportSession key)1922 private @Nullable SipTransportSession find(SipTransportSession key) { 1923 for (SipTransportSession stats : mAtoms.sipTransportSession) { 1924 if (stats.carrierId == key.carrierId 1925 && stats.slotId == key.slotId 1926 && stats.sessionMethod == key.sessionMethod 1927 && stats.sipMessageDirection == key.sipMessageDirection 1928 && stats.sipResponse == key.sipResponse) { 1929 return stats; 1930 } 1931 } 1932 return null; 1933 } 1934 1935 /** 1936 * Returns Registration Service Desc Stats that has the same carrier id, slot id, service id or 1937 * custom service id, service id version and registration tech as the given one, 1938 * or {@code null} if it does not exist. 1939 */ find(ImsRegistrationServiceDescStats key)1940 private @Nullable ImsRegistrationServiceDescStats find(ImsRegistrationServiceDescStats key) { 1941 for (ImsRegistrationServiceDescStats stats : mAtoms.imsRegistrationServiceDescStats) { 1942 if (stats.carrierId == key.carrierId 1943 && stats.slotId == key.slotId 1944 && stats.serviceIdName == key.serviceIdName 1945 && stats.serviceIdVersion == key.serviceIdVersion 1946 && stats.registrationTech == key.registrationTech) { 1947 return stats; 1948 } 1949 } 1950 return null; 1951 } 1952 1953 /** 1954 * Returns UCE Event Stats that has the same carrier id, slot id, event result, command code and 1955 * network response as the given one, or {@code null} if it does not exist. 1956 */ find(UceEventStats key)1957 private @Nullable UceEventStats find(UceEventStats key) { 1958 for (UceEventStats stats : mAtoms.uceEventStats) { 1959 if (stats.carrierId == key.carrierId 1960 && stats.slotId == key.slotId 1961 && stats.type == key.type 1962 && stats.successful == key.successful 1963 && stats.commandCode == key.commandCode 1964 && stats.networkResponse == key.networkResponse) { 1965 return stats; 1966 } 1967 } 1968 return null; 1969 } 1970 1971 /** 1972 * Returns Presence Notify Event that has the same carrier id, slot id, reason and body in 1973 * response as the given one, or {@code null} if it does not exist. 1974 */ find(PresenceNotifyEvent key)1975 private @Nullable PresenceNotifyEvent find(PresenceNotifyEvent key) { 1976 for (PresenceNotifyEvent stats : mAtoms.presenceNotifyEvent) { 1977 if (stats.carrierId == key.carrierId 1978 && stats.slotId == key.slotId 1979 && stats.reason == key.reason 1980 && stats.contentBodyReceived == key.contentBodyReceived) { 1981 return stats; 1982 } 1983 } 1984 return null; 1985 } 1986 1987 /** 1988 * Returns GBA Event that has the same carrier id, slot id, result of operation and fail reason 1989 * as the given one, or {@code null} if it does not exist. 1990 */ find(GbaEvent key)1991 private @Nullable GbaEvent find(GbaEvent key) { 1992 for (GbaEvent stats : mAtoms.gbaEvent) { 1993 if (stats.carrierId == key.carrierId 1994 && stats.slotId == key.slotId 1995 && stats.successful == key.successful 1996 && stats.failedReason == key.failedReason) { 1997 return stats; 1998 } 1999 } 2000 return null; 2001 } 2002 2003 /** 2004 * Returns Sip Transport Feature Tag Stats that has the same carrier id, slot id, feature tag 2005 * name, deregister reason, denied reason and feature tag name or custom feature tag name as 2006 * the given one, or {@code null} if it does not exist. 2007 */ find(SipTransportFeatureTagStats key)2008 private @Nullable SipTransportFeatureTagStats find(SipTransportFeatureTagStats key) { 2009 for (SipTransportFeatureTagStats stat : mAtoms.sipTransportFeatureTagStats) { 2010 if (stat.carrierId == key.carrierId 2011 && stat.slotId == key.slotId 2012 && stat.featureTagName == key.featureTagName 2013 && stat.sipTransportDeregisteredReason == key.sipTransportDeregisteredReason 2014 && stat.sipTransportDeniedReason == key.sipTransportDeniedReason) { 2015 return stat; 2016 } 2017 } 2018 return null; 2019 } 2020 2021 /** Returns the UnmeteredNetworks given a phone id. */ findUnmeteredNetworks(int phoneId)2022 private @Nullable UnmeteredNetworks findUnmeteredNetworks(int phoneId) { 2023 for (UnmeteredNetworks unmeteredNetworks : mAtoms.unmeteredNetworks) { 2024 if (unmeteredNetworks.phoneId == phoneId) { 2025 return unmeteredNetworks; 2026 } 2027 } 2028 return null; 2029 } 2030 2031 /** 2032 * Returns OutgoingShortCodeSms atom that has same category, xmlVersion as the given one, 2033 * or {@code null} if it does not exist. 2034 */ find(OutgoingShortCodeSms key)2035 private @Nullable OutgoingShortCodeSms find(OutgoingShortCodeSms key) { 2036 for (OutgoingShortCodeSms shortCodeSms : mAtoms.outgoingShortCodeSms) { 2037 if (shortCodeSms.category == key.category 2038 && shortCodeSms.xmlVersion == key.xmlVersion) { 2039 return shortCodeSms; 2040 } 2041 } 2042 return null; 2043 } 2044 2045 /** 2046 * Returns SatelliteOutgoingDatagram atom that has same values or {@code null} 2047 * if it does not exist. 2048 */ find( SatelliteSession key)2049 private @Nullable SatelliteSession find( 2050 SatelliteSession key) { 2051 for (SatelliteSession stats : mAtoms.satelliteSession) { 2052 if (stats.satelliteServiceInitializationResult 2053 == key.satelliteServiceInitializationResult 2054 && stats.satelliteTechnology == key.satelliteTechnology) { 2055 return stats; 2056 } 2057 } 2058 return null; 2059 } 2060 2061 /** 2062 * Returns SatelliteOutgoingDatagram atom that has same values or {@code null} 2063 * if it does not exist. 2064 */ find( SatelliteSosMessageRecommender key)2065 private @Nullable SatelliteSosMessageRecommender find( 2066 SatelliteSosMessageRecommender key) { 2067 for (SatelliteSosMessageRecommender stats : mAtoms.satelliteSosMessageRecommender) { 2068 if (stats.isDisplaySosMessageSent == key.isDisplaySosMessageSent 2069 && stats.countOfTimerStarted == key.countOfTimerStarted 2070 && stats.isImsRegistered == key.isImsRegistered 2071 && stats.cellularServiceState == key.cellularServiceState) { 2072 return stats; 2073 } 2074 } 2075 return null; 2076 } 2077 2078 /** 2079 * Inserts a new element in a random position in an array with a maximum size. 2080 * 2081 * <p>If the array is full, merge with existing item if possible or replace one item randomly. 2082 */ insertAtRandomPlace(T[] storage, T instance, int maxLength)2083 private static <T> T[] insertAtRandomPlace(T[] storage, T instance, int maxLength) { 2084 final int newLength = storage.length + 1; 2085 final boolean arrayFull = (newLength > maxLength); 2086 T[] result = Arrays.copyOf(storage, arrayFull ? maxLength : newLength); 2087 if (newLength == 1) { 2088 result[0] = instance; 2089 } else if (arrayFull) { 2090 if (instance instanceof OutgoingSms || instance instanceof IncomingSms) { 2091 mergeSmsOrEvictInFullStorage(result, instance); 2092 } else { 2093 result[findItemToEvict(storage)] = instance; 2094 } 2095 } else { 2096 // insert at random place (by moving the item at the random place to the end) 2097 int insertAt = sRandom.nextInt(newLength); 2098 result[newLength - 1] = result[insertAt]; 2099 result[insertAt] = instance; 2100 } 2101 return result; 2102 } 2103 2104 /** 2105 * Merge new sms in a full storage. 2106 * 2107 * <p>If new sms is similar to old sms, merge them. 2108 * If not, merge 2 old similar sms and add the new sms. 2109 * If not, replace old sms with the lowest count. 2110 */ mergeSmsOrEvictInFullStorage(T[] storage, T instance)2111 private static <T> void mergeSmsOrEvictInFullStorage(T[] storage, T instance) { 2112 // key: hashCode, value: smsIndex 2113 SparseIntArray map = new SparseIntArray(); 2114 int smsIndex1 = -1; 2115 int smsIndex2 = -1; 2116 int indexLowestCount = -1; 2117 int minCount = Integer.MAX_VALUE; 2118 2119 for (int i = 0; i < storage.length; i++) { 2120 // If the new SMS can be merged to an existing item, merge it and return immediately. 2121 if (areSmsMergeable(storage[i], instance)) { 2122 storage[i] = mergeSms(storage[i], instance); 2123 return; 2124 } 2125 2126 // Keep sms index with lowest count to evict, in case we cannot merge any 2 messages. 2127 int smsCount = getSmsCount(storage[i]); 2128 if (smsCount < minCount) { 2129 indexLowestCount = i; 2130 minCount = smsCount; 2131 } 2132 2133 // Find any 2 messages in the storage that can be merged together. 2134 if (smsIndex1 != -1) { 2135 int smsHashCode = getSmsHashCode(storage[i]); 2136 if (map.indexOfKey(smsHashCode) < 0) { 2137 map.append(smsHashCode, i); 2138 } else { 2139 smsIndex1 = map.get(smsHashCode); 2140 smsIndex2 = i; 2141 } 2142 } 2143 } 2144 2145 // Merge 2 similar old sms and add the new sms 2146 if (smsIndex1 != -1) { 2147 storage[smsIndex1] = mergeSms(storage[smsIndex1], storage[smsIndex2]); 2148 storage[smsIndex2] = instance; 2149 return; 2150 } 2151 2152 // Or replace old sms that has the lowest count 2153 storage[indexLowestCount] = instance; 2154 return; 2155 } 2156 getSmsHashCode(T sms)2157 private static <T> int getSmsHashCode(T sms) { 2158 return sms instanceof OutgoingSms 2159 ? ((OutgoingSms) sms).hashCode : ((IncomingSms) sms).hashCode; 2160 } 2161 getSmsCount(T sms)2162 private static <T> int getSmsCount(T sms) { 2163 return sms instanceof OutgoingSms 2164 ? ((OutgoingSms) sms).count : ((IncomingSms) sms).count; 2165 } 2166 2167 /** Compares 2 SMS hash codes to check if they can be clubbed together in the metrics. */ areSmsMergeable(T instance1, T instance2)2168 private static <T> boolean areSmsMergeable(T instance1, T instance2) { 2169 return getSmsHashCode(instance1) == getSmsHashCode(instance2); 2170 } 2171 2172 /** Merges sms2 data on top of sms1 and returns the merged value. */ mergeSms(T sms1, T sms2)2173 private static <T> T mergeSms(T sms1, T sms2) { 2174 if (sms1 instanceof OutgoingSms) { 2175 OutgoingSms tSms1 = (OutgoingSms) sms1; 2176 OutgoingSms tSms2 = (OutgoingSms) sms2; 2177 tSms1.intervalMillis = (tSms1.intervalMillis * tSms1.count 2178 + tSms2.intervalMillis * tSms2.count) / (tSms1.count + tSms2.count); 2179 tSms1.count += tSms2.count; 2180 } else if (sms1 instanceof IncomingSms) { 2181 IncomingSms tSms1 = (IncomingSms) sms1; 2182 IncomingSms tSms2 = (IncomingSms) sms2; 2183 tSms1.count += tSms2.count; 2184 } 2185 return sms1; 2186 } 2187 2188 /** Returns index of the item suitable for eviction when the array is full. */ findItemToEvict(T[] array)2189 private static <T> int findItemToEvict(T[] array) { 2190 if (array instanceof CellularServiceState[]) { 2191 // Evict the item that was used least recently 2192 CellularServiceState[] arr = (CellularServiceState[]) array; 2193 return IntStream.range(0, arr.length) 2194 .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j) 2195 .getAsInt(); 2196 } 2197 2198 if (array instanceof CellularDataServiceSwitch[]) { 2199 // Evict the item that was used least recently 2200 CellularDataServiceSwitch[] arr = (CellularDataServiceSwitch[]) array; 2201 return IntStream.range(0, arr.length) 2202 .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j) 2203 .getAsInt(); 2204 } 2205 2206 if (array instanceof ImsRegistrationStats[]) { 2207 // Evict the item that was used least recently 2208 ImsRegistrationStats[] arr = (ImsRegistrationStats[]) array; 2209 return IntStream.range(0, arr.length) 2210 .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j) 2211 .getAsInt(); 2212 } 2213 2214 if (array instanceof ImsRegistrationTermination[]) { 2215 // Evict the item that was used least recently 2216 ImsRegistrationTermination[] arr = (ImsRegistrationTermination[]) array; 2217 return IntStream.range(0, arr.length) 2218 .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j) 2219 .getAsInt(); 2220 } 2221 2222 if (array instanceof VoiceCallSession[]) { 2223 // For voice calls, try to keep emergency calls over regular calls. 2224 VoiceCallSession[] arr = (VoiceCallSession[]) array; 2225 int[] nonEmergencyCallIndexes = IntStream.range(0, arr.length) 2226 .filter(i -> !arr[i].isEmergency) 2227 .toArray(); 2228 if (nonEmergencyCallIndexes.length > 0) { 2229 return nonEmergencyCallIndexes[sRandom.nextInt(nonEmergencyCallIndexes.length)]; 2230 } 2231 // If all calls in the storage are emergency calls, proceed with default case 2232 // even if the new call is not an emergency call. 2233 } 2234 2235 return sRandom.nextInt(array.length); 2236 } 2237 2238 /** Sanitizes the loaded array of atoms to avoid null values. */ sanitizeAtoms(T[] array, Class<T> cl)2239 private <T> T[] sanitizeAtoms(T[] array, Class<T> cl) { 2240 return ArrayUtils.emptyIfNull(array, cl); 2241 } 2242 2243 /** Sanitizes the loaded array of atoms loaded to avoid null values and enforce max length. */ sanitizeAtoms(T[] array, Class<T> cl, int maxLength)2244 private <T> T[] sanitizeAtoms(T[] array, Class<T> cl, int maxLength) { 2245 array = sanitizeAtoms(array, cl); 2246 if (array.length > maxLength) { 2247 return Arrays.copyOf(array, maxLength); 2248 } 2249 return array; 2250 } 2251 2252 /** Sanitizes the timestamp of the last pull loaded from persistent storage. */ sanitizeTimestamp(long timestamp)2253 private long sanitizeTimestamp(long timestamp) { 2254 return timestamp <= 0L ? getWallTimeMillis() : timestamp; 2255 } 2256 2257 /** 2258 * Returns {@link ImsRegistrationStats} array with durations normalized to 24 hours 2259 * depending on the interval. 2260 */ normalizeData(ImsRegistrationStats[] stats, long intervalMillis)2261 private ImsRegistrationStats[] normalizeData(ImsRegistrationStats[] stats, 2262 long intervalMillis) { 2263 for (int i = 0; i < stats.length; i++) { 2264 stats[i].registeredMillis = 2265 normalizeDurationTo24H(stats[i].registeredMillis, intervalMillis); 2266 stats[i].voiceCapableMillis = 2267 normalizeDurationTo24H(stats[i].voiceCapableMillis, intervalMillis); 2268 stats[i].voiceAvailableMillis = 2269 normalizeDurationTo24H(stats[i].voiceAvailableMillis, intervalMillis); 2270 stats[i].smsCapableMillis = 2271 normalizeDurationTo24H(stats[i].smsCapableMillis, intervalMillis); 2272 stats[i].smsAvailableMillis = 2273 normalizeDurationTo24H(stats[i].smsAvailableMillis, intervalMillis); 2274 stats[i].videoCapableMillis = 2275 normalizeDurationTo24H(stats[i].videoCapableMillis, intervalMillis); 2276 stats[i].videoAvailableMillis = 2277 normalizeDurationTo24H(stats[i].videoAvailableMillis, intervalMillis); 2278 stats[i].utCapableMillis = 2279 normalizeDurationTo24H(stats[i].utCapableMillis, intervalMillis); 2280 stats[i].utAvailableMillis = 2281 normalizeDurationTo24H(stats[i].utAvailableMillis, intervalMillis); 2282 } 2283 return stats; 2284 } 2285 2286 /** Returns a duration normalized to 24 hours. */ normalizeDurationTo24H(long timeInMillis, long intervalMillis)2287 private long normalizeDurationTo24H(long timeInMillis, long intervalMillis) { 2288 long interval = intervalMillis < 1000 ? 1 : intervalMillis / 1000; 2289 return ((timeInMillis / 1000) * (DAY_IN_MILLIS / 1000) / interval) * 1000; 2290 } 2291 2292 /** Returns an empty PersistAtoms with pull timestamp set to current time. */ 2293 private PersistAtoms makeNewPersistAtoms() { 2294 PersistAtoms atoms = new PersistAtoms(); 2295 // allow pulling only after some time so data are sufficiently aggregated 2296 long currentTime = getWallTimeMillis(); 2297 atoms.buildFingerprint = Build.FINGERPRINT; 2298 atoms.voiceCallRatUsagePullTimestampMillis = currentTime; 2299 atoms.voiceCallSessionPullTimestampMillis = currentTime; 2300 atoms.incomingSmsPullTimestampMillis = currentTime; 2301 atoms.outgoingSmsPullTimestampMillis = currentTime; 2302 atoms.carrierIdTableVersion = TelephonyManager.UNKNOWN_CARRIER_ID_LIST_VERSION; 2303 atoms.dataCallSessionPullTimestampMillis = currentTime; 2304 atoms.cellularServiceStatePullTimestampMillis = currentTime; 2305 atoms.cellularDataServiceSwitchPullTimestampMillis = currentTime; 2306 atoms.imsRegistrationStatsPullTimestampMillis = currentTime; 2307 atoms.imsRegistrationTerminationPullTimestampMillis = currentTime; 2308 atoms.networkRequestsPullTimestampMillis = currentTime; 2309 atoms.networkRequestsV2PullTimestampMillis = currentTime; 2310 atoms.imsRegistrationFeatureTagStatsPullTimestampMillis = currentTime; 2311 atoms.rcsClientProvisioningStatsPullTimestampMillis = currentTime; 2312 atoms.rcsAcsProvisioningStatsPullTimestampMillis = currentTime; 2313 atoms.sipDelegateStatsPullTimestampMillis = currentTime; 2314 atoms.sipTransportFeatureTagStatsPullTimestampMillis = currentTime; 2315 atoms.sipMessageResponsePullTimestampMillis = currentTime; 2316 atoms.sipTransportSessionPullTimestampMillis = currentTime; 2317 atoms.imsDedicatedBearerListenerEventPullTimestampMillis = currentTime; 2318 atoms.imsDedicatedBearerEventPullTimestampMillis = currentTime; 2319 atoms.imsRegistrationServiceDescStatsPullTimestampMillis = currentTime; 2320 atoms.uceEventStatsPullTimestampMillis = currentTime; 2321 atoms.presenceNotifyEventPullTimestampMillis = currentTime; 2322 atoms.gbaEventPullTimestampMillis = currentTime; 2323 atoms.outgoingShortCodeSmsPullTimestampMillis = currentTime; 2324 atoms.satelliteControllerPullTimestampMillis = currentTime; 2325 atoms.satelliteSessionPullTimestampMillis = currentTime; 2326 atoms.satelliteIncomingDatagramPullTimestampMillis = currentTime; 2327 atoms.satelliteOutgoingDatagramPullTimestampMillis = currentTime; 2328 atoms.satelliteProvisionPullTimestampMillis = currentTime; 2329 atoms.satelliteSosMessageRecommenderPullTimestampMillis = currentTime; 2330 2331 Rlog.d(TAG, "created new PersistAtoms"); 2332 return atoms; 2333 } 2334 2335 @VisibleForTesting 2336 protected long getWallTimeMillis() { 2337 // Epoch time in UTC, preserved across reboots, but can be adjusted e.g. by the user or NTP 2338 return System.currentTimeMillis(); 2339 } 2340 } 2341