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