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