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 android.annotation.Nullable; 20 import android.content.Context; 21 import android.os.Build; 22 import android.os.Handler; 23 import android.os.HandlerThread; 24 import android.telephony.TelephonyManager; 25 26 import com.android.internal.annotations.VisibleForTesting; 27 import com.android.internal.telephony.nano.PersistAtomsProto.CarrierIdMismatch; 28 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch; 29 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState; 30 import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession; 31 import com.android.internal.telephony.nano.PersistAtomsProto.GbaEvent; 32 import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerEvent; 33 import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerListenerEvent; 34 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationFeatureTagStats; 35 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationServiceDescStats; 36 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats; 37 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination; 38 import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms; 39 import com.android.internal.telephony.nano.PersistAtomsProto.NetworkRequests; 40 import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms; 41 import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms; 42 import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent; 43 import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats; 44 import com.android.internal.telephony.nano.PersistAtomsProto.RcsClientProvisioningStats; 45 import com.android.internal.telephony.nano.PersistAtomsProto.SipDelegateStats; 46 import com.android.internal.telephony.nano.PersistAtomsProto.SipMessageResponse; 47 import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportFeatureTagStats; 48 import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportSession; 49 import com.android.internal.telephony.nano.PersistAtomsProto.UceEventStats; 50 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage; 51 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession; 52 import com.android.internal.util.ArrayUtils; 53 import com.android.telephony.Rlog; 54 55 import java.io.FileOutputStream; 56 import java.io.IOException; 57 import java.nio.file.Files; 58 import java.nio.file.NoSuchFileException; 59 import java.security.SecureRandom; 60 import java.util.Arrays; 61 import java.util.stream.IntStream; 62 63 /** 64 * Stores and aggregates metrics that should not be pulled at arbitrary frequency. 65 * 66 * <p>NOTE: while this class checks timestamp against {@code minIntervalMillis}, it is {@link 67 * MetricsCollector}'s responsibility to ensure {@code minIntervalMillis} is set correctly. 68 */ 69 public class PersistAtomsStorage { 70 private static final String TAG = PersistAtomsStorage.class.getSimpleName(); 71 72 /** Name of the file where cached statistics are saved to. */ 73 private static final String FILENAME = "persist_atoms.pb"; 74 75 /** Delay to store atoms to persistent storage to bundle multiple operations together. */ 76 private static final int SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS = 30000; 77 78 /** 79 * Delay to store atoms to persistent storage during pulls to avoid unnecessary operations. 80 * 81 * <p>This delay should be short to avoid duplicating atoms or losing pull timestamp in case of 82 * crash or power loss. 83 */ 84 private static final int SAVE_TO_FILE_DELAY_FOR_GET_MILLIS = 500; 85 86 /** Maximum number of call sessions to store between pulls. */ 87 private static final int MAX_NUM_CALL_SESSIONS = 50; 88 89 /** 90 * Maximum number of SMS to store between pulls. Incoming messages and outgoing messages are 91 * counted separately. 92 */ 93 private static final int MAX_NUM_SMS = 25; 94 95 /** 96 * Maximum number of carrier ID mismatch events stored on the device to avoid sending duplicated 97 * metrics. 98 */ 99 private static final int MAX_CARRIER_ID_MISMATCH = 40; 100 101 /** Maximum number of data call sessions to store during pulls. */ 102 private static final int MAX_NUM_DATA_CALL_SESSIONS = 15; 103 104 /** Maximum number of service states to store between pulls. */ 105 private static final int MAX_NUM_CELLULAR_SERVICE_STATES = 50; 106 107 /** Maximum number of data service switches to store between pulls. */ 108 private static final int MAX_NUM_CELLULAR_DATA_SERVICE_SWITCHES = 50; 109 110 /** Maximum number of IMS registration stats to store between pulls. */ 111 private static final int MAX_NUM_IMS_REGISTRATION_STATS = 10; 112 113 /** Maximum number of IMS registration terminations to store between pulls. */ 114 private static final int MAX_NUM_IMS_REGISTRATION_TERMINATIONS = 10; 115 116 /** Maximum number of IMS Registration Feature Tags to store between pulls. */ 117 private static final int MAX_NUM_IMS_REGISTRATION_FEATURE_STATS = 25; 118 119 /** Maximum number of RCS Client Provisioning to store between pulls. */ 120 private static final int MAX_NUM_RCS_CLIENT_PROVISIONING_STATS = 10; 121 122 /** Maximum number of RCS Acs Provisioning to store between pulls. */ 123 private static final int MAX_NUM_RCS_ACS_PROVISIONING_STATS = 10; 124 125 /** Maximum number of Sip Message Response to store between pulls. */ 126 private static final int MAX_NUM_SIP_MESSAGE_RESPONSE_STATS = 25; 127 128 /** Maximum number of Sip Transport Session to store between pulls. */ 129 private static final int MAX_NUM_SIP_TRANSPORT_SESSION_STATS = 25; 130 131 /** Maximum number of Sip Delegate to store between pulls. */ 132 private static final int MAX_NUM_SIP_DELEGATE_STATS = 10; 133 134 /** Maximum number of Sip Transport Feature Tag to store between pulls. */ 135 private static final int MAX_NUM_SIP_TRANSPORT_FEATURE_TAG_STATS = 25; 136 137 /** Maximum number of Dedicated Bearer Listener Event to store between pulls. */ 138 private static final int MAX_NUM_DEDICATED_BEARER_LISTENER_EVENT_STATS = 10; 139 140 /** Maximum number of Dedicated Bearer Event to store between pulls. */ 141 private static final int MAX_NUM_DEDICATED_BEARER_EVENT_STATS = 10; 142 143 /** Maximum number of IMS Registration Service Desc to store between pulls. */ 144 private static final int MAX_NUM_IMS_REGISTRATION_SERVICE_DESC_STATS = 25; 145 146 /** Maximum number of UCE Event to store between pulls. */ 147 private static final int MAX_NUM_UCE_EVENT_STATS = 25; 148 149 /** Maximum number of Presence Notify Event to store between pulls. */ 150 private static final int MAX_NUM_PRESENCE_NOTIFY_EVENT_STATS = 50; 151 152 /** Maximum number of GBA Event to store between pulls. */ 153 private static final int MAX_NUM_GBA_EVENT_STATS = 10; 154 155 /** Stores persist atoms and persist states of the puller. */ 156 @VisibleForTesting protected final PersistAtoms mAtoms; 157 158 /** Aggregates RAT duration and call count. */ 159 private final VoiceCallRatTracker mVoiceCallRatTracker; 160 161 /** Whether atoms should be saved immediately, skipping the delay. */ 162 @VisibleForTesting protected boolean mSaveImmediately; 163 164 private final Context mContext; 165 private final Handler mHandler; 166 private final HandlerThread mHandlerThread; 167 private static final SecureRandom sRandom = new SecureRandom(); 168 169 private Runnable mSaveRunnable = 170 new Runnable() { 171 @Override 172 public void run() { 173 saveAtomsToFileNow(); 174 } 175 }; 176 PersistAtomsStorage(Context context)177 public PersistAtomsStorage(Context context) { 178 mContext = context; 179 mAtoms = loadAtomsFromFile(); 180 mVoiceCallRatTracker = VoiceCallRatTracker.fromProto(mAtoms.voiceCallRatUsage); 181 182 mHandlerThread = new HandlerThread("PersistAtomsThread"); 183 mHandlerThread.start(); 184 mHandler = new Handler(mHandlerThread.getLooper()); 185 mSaveImmediately = false; 186 } 187 188 /** Adds a call to the storage. */ addVoiceCallSession(VoiceCallSession call)189 public synchronized void addVoiceCallSession(VoiceCallSession call) { 190 mAtoms.voiceCallSession = 191 insertAtRandomPlace(mAtoms.voiceCallSession, call, MAX_NUM_CALL_SESSIONS); 192 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 193 194 Rlog.d(TAG, "Add new voice call session: " + call.toString()); 195 } 196 197 /** Adds RAT usages to the storage when a call session ends. */ addVoiceCallRatUsage(VoiceCallRatTracker ratUsages)198 public synchronized void addVoiceCallRatUsage(VoiceCallRatTracker ratUsages) { 199 mVoiceCallRatTracker.mergeWith(ratUsages); 200 mAtoms.voiceCallRatUsage = mVoiceCallRatTracker.toProto(); 201 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 202 } 203 204 /** Adds an incoming SMS to the storage. */ addIncomingSms(IncomingSms sms)205 public synchronized void addIncomingSms(IncomingSms sms) { 206 mAtoms.incomingSms = insertAtRandomPlace(mAtoms.incomingSms, sms, MAX_NUM_SMS); 207 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 208 209 // To be removed 210 Rlog.d(TAG, "Add new incoming SMS atom: " + sms.toString()); 211 } 212 213 /** Adds an outgoing SMS to the storage. */ addOutgoingSms(OutgoingSms sms)214 public synchronized void addOutgoingSms(OutgoingSms sms) { 215 // Update the retry id, if needed, so that it's unique and larger than all 216 // previous ones. (this algorithm ignores the fact that some SMS atoms might 217 // be dropped due to limit in size of the array). 218 for (OutgoingSms storedSms : mAtoms.outgoingSms) { 219 if (storedSms.messageId == sms.messageId && storedSms.retryId >= sms.retryId) { 220 sms.retryId = storedSms.retryId + 1; 221 } 222 } 223 224 mAtoms.outgoingSms = insertAtRandomPlace(mAtoms.outgoingSms, sms, MAX_NUM_SMS); 225 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 226 227 // To be removed 228 Rlog.d(TAG, "Add new outgoing SMS atom: " + sms.toString()); 229 } 230 231 /** Adds a service state to the storage, together with data service switch if any. */ addCellularServiceStateAndCellularDataServiceSwitch( CellularServiceState state, @Nullable CellularDataServiceSwitch serviceSwitch)232 public synchronized void addCellularServiceStateAndCellularDataServiceSwitch( 233 CellularServiceState state, @Nullable CellularDataServiceSwitch serviceSwitch) { 234 CellularServiceState existingState = find(state); 235 if (existingState != null) { 236 existingState.totalTimeMillis += state.totalTimeMillis; 237 existingState.lastUsedMillis = getWallTimeMillis(); 238 } else { 239 state.lastUsedMillis = getWallTimeMillis(); 240 mAtoms.cellularServiceState = 241 insertAtRandomPlace( 242 mAtoms.cellularServiceState, state, MAX_NUM_CELLULAR_SERVICE_STATES); 243 } 244 245 if (serviceSwitch != null) { 246 CellularDataServiceSwitch existingSwitch = find(serviceSwitch); 247 if (existingSwitch != null) { 248 existingSwitch.switchCount += serviceSwitch.switchCount; 249 existingSwitch.lastUsedMillis = getWallTimeMillis(); 250 } else { 251 serviceSwitch.lastUsedMillis = getWallTimeMillis(); 252 mAtoms.cellularDataServiceSwitch = 253 insertAtRandomPlace( 254 mAtoms.cellularDataServiceSwitch, 255 serviceSwitch, 256 MAX_NUM_CELLULAR_DATA_SERVICE_SWITCHES); 257 } 258 } 259 260 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 261 } 262 263 /** Adds a data call session to the storage. */ addDataCallSession(DataCallSession dataCall)264 public synchronized void addDataCallSession(DataCallSession dataCall) { 265 int index = findIndex(dataCall); 266 if (index >= 0) { 267 DataCallSession existingCall = mAtoms.dataCallSession[index]; 268 dataCall.ratSwitchCount += existingCall.ratSwitchCount; 269 dataCall.durationMinutes += existingCall.durationMinutes; 270 mAtoms.dataCallSession[index] = dataCall; 271 } else { 272 mAtoms.dataCallSession = 273 insertAtRandomPlace( 274 mAtoms.dataCallSession, dataCall, MAX_NUM_DATA_CALL_SESSIONS); 275 } 276 277 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 278 } 279 280 /** 281 * Adds a new carrier ID mismatch event to the storage. 282 * 283 * @return true if the item was not present and was added to the persistent storage, false 284 * otherwise. 285 */ addCarrierIdMismatch(CarrierIdMismatch carrierIdMismatch)286 public synchronized boolean addCarrierIdMismatch(CarrierIdMismatch carrierIdMismatch) { 287 // Check if the details of the SIM cards are already present and in case return. 288 if (find(carrierIdMismatch) != null) { 289 return false; 290 } 291 // Add the new CarrierIdMismatch at the end of the array, so that the same atom will not be 292 // sent again in future. 293 if (mAtoms.carrierIdMismatch.length == MAX_CARRIER_ID_MISMATCH) { 294 System.arraycopy( 295 mAtoms.carrierIdMismatch, 296 1, 297 mAtoms.carrierIdMismatch, 298 0, 299 MAX_CARRIER_ID_MISMATCH - 1); 300 mAtoms.carrierIdMismatch[MAX_CARRIER_ID_MISMATCH - 1] = carrierIdMismatch; 301 } else { 302 int newLength = mAtoms.carrierIdMismatch.length + 1; 303 mAtoms.carrierIdMismatch = Arrays.copyOf(mAtoms.carrierIdMismatch, newLength); 304 mAtoms.carrierIdMismatch[newLength - 1] = carrierIdMismatch; 305 } 306 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 307 return true; 308 } 309 310 /** Adds IMS registration stats to the storage. */ addImsRegistrationStats(ImsRegistrationStats stats)311 public synchronized void addImsRegistrationStats(ImsRegistrationStats stats) { 312 ImsRegistrationStats existingStats = find(stats); 313 if (existingStats != null) { 314 existingStats.registeredMillis += stats.registeredMillis; 315 existingStats.voiceCapableMillis += stats.voiceCapableMillis; 316 existingStats.voiceAvailableMillis += stats.voiceAvailableMillis; 317 existingStats.smsCapableMillis += stats.smsCapableMillis; 318 existingStats.smsAvailableMillis += stats.smsAvailableMillis; 319 existingStats.videoCapableMillis += stats.videoCapableMillis; 320 existingStats.videoAvailableMillis += stats.videoAvailableMillis; 321 existingStats.utCapableMillis += stats.utCapableMillis; 322 existingStats.utAvailableMillis += stats.utAvailableMillis; 323 existingStats.lastUsedMillis = getWallTimeMillis(); 324 } else { 325 stats.lastUsedMillis = getWallTimeMillis(); 326 mAtoms.imsRegistrationStats = 327 insertAtRandomPlace( 328 mAtoms.imsRegistrationStats, stats, MAX_NUM_IMS_REGISTRATION_STATS); 329 } 330 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 331 } 332 333 /** Adds IMS registration termination to the storage. */ addImsRegistrationTermination(ImsRegistrationTermination termination)334 public synchronized void addImsRegistrationTermination(ImsRegistrationTermination termination) { 335 ImsRegistrationTermination existingTermination = find(termination); 336 if (existingTermination != null) { 337 existingTermination.count += termination.count; 338 existingTermination.lastUsedMillis = getWallTimeMillis(); 339 } else { 340 termination.lastUsedMillis = getWallTimeMillis(); 341 mAtoms.imsRegistrationTermination = 342 insertAtRandomPlace( 343 mAtoms.imsRegistrationTermination, 344 termination, 345 MAX_NUM_IMS_REGISTRATION_TERMINATIONS); 346 } 347 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 348 } 349 350 /** 351 * Stores the version of the carrier ID matching table. 352 * 353 * @return true if the version is newer than last available version, false otherwise. 354 */ setCarrierIdTableVersion(int carrierIdTableVersion)355 public synchronized boolean setCarrierIdTableVersion(int carrierIdTableVersion) { 356 if (mAtoms.carrierIdTableVersion < carrierIdTableVersion) { 357 mAtoms.carrierIdTableVersion = carrierIdTableVersion; 358 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 359 return true; 360 } else { 361 return false; 362 } 363 } 364 365 /** Adds a new {@link NetworkRequests} to the storage. */ addNetworkRequests(NetworkRequests networkRequests)366 public synchronized void addNetworkRequests(NetworkRequests networkRequests) { 367 NetworkRequests existingMetrics = find(networkRequests); 368 if (existingMetrics != null) { 369 existingMetrics.enterpriseRequestCount += networkRequests.enterpriseRequestCount; 370 existingMetrics.enterpriseReleaseCount += networkRequests.enterpriseReleaseCount; 371 } else { 372 int newLength = mAtoms.networkRequests.length + 1; 373 mAtoms.networkRequests = Arrays.copyOf(mAtoms.networkRequests, newLength); 374 mAtoms.networkRequests[newLength - 1] = networkRequests; 375 } 376 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 377 } 378 379 /** Adds a new {@link ImsRegistrationFeatureTagStats} to the storage. */ addImsRegistrationFeatureTagStats( ImsRegistrationFeatureTagStats stats)380 public synchronized void addImsRegistrationFeatureTagStats( 381 ImsRegistrationFeatureTagStats stats) { 382 ImsRegistrationFeatureTagStats existingStats = find(stats); 383 if (existingStats != null) { 384 existingStats.registeredMillis += stats.registeredMillis; 385 } else { 386 mAtoms.imsRegistrationFeatureTagStats = 387 insertAtRandomPlace(mAtoms.imsRegistrationFeatureTagStats, 388 stats, MAX_NUM_IMS_REGISTRATION_FEATURE_STATS); 389 } 390 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 391 } 392 393 /** Adds a new {@link RcsClientProvisioningStats} to the storage. */ addRcsClientProvisioningStats(RcsClientProvisioningStats stats)394 public synchronized void addRcsClientProvisioningStats(RcsClientProvisioningStats stats) { 395 RcsClientProvisioningStats existingStats = find(stats); 396 if (existingStats != null) { 397 existingStats.count += 1; 398 } else { 399 mAtoms.rcsClientProvisioningStats = 400 insertAtRandomPlace(mAtoms.rcsClientProvisioningStats, stats, 401 MAX_NUM_RCS_CLIENT_PROVISIONING_STATS); 402 } 403 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 404 } 405 406 /** Adds a new {@link RcsAcsProvisioningStats} to the storage. */ addRcsAcsProvisioningStats(RcsAcsProvisioningStats stats)407 public synchronized void addRcsAcsProvisioningStats(RcsAcsProvisioningStats stats) { 408 RcsAcsProvisioningStats existingStats = find(stats); 409 if (existingStats != null) { 410 existingStats.count += 1; 411 existingStats.stateTimerMillis += stats.stateTimerMillis; 412 } else { 413 // prevent that wrong count from caller effects total count 414 stats.count = 1; 415 mAtoms.rcsAcsProvisioningStats = 416 insertAtRandomPlace(mAtoms.rcsAcsProvisioningStats, stats, 417 MAX_NUM_RCS_ACS_PROVISIONING_STATS); 418 } 419 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 420 } 421 422 /** Adds a new {@link SipDelegateStats} to the storage. */ addSipDelegateStats(SipDelegateStats stats)423 public synchronized void addSipDelegateStats(SipDelegateStats stats) { 424 mAtoms.sipDelegateStats = insertAtRandomPlace(mAtoms.sipDelegateStats, stats, 425 MAX_NUM_SIP_DELEGATE_STATS); 426 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 427 } 428 429 /** Adds a new {@link SipTransportFeatureTagStats} to the storage. */ addSipTransportFeatureTagStats(SipTransportFeatureTagStats stats)430 public synchronized void addSipTransportFeatureTagStats(SipTransportFeatureTagStats stats) { 431 SipTransportFeatureTagStats lastStat = find(stats); 432 if (lastStat != null) { 433 lastStat.associatedMillis += stats.associatedMillis; 434 } else { 435 mAtoms.sipTransportFeatureTagStats = 436 insertAtRandomPlace(mAtoms.sipTransportFeatureTagStats, stats, 437 MAX_NUM_SIP_TRANSPORT_FEATURE_TAG_STATS); 438 } 439 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 440 } 441 442 /** Adds a new {@link SipMessageResponse} to the storage. */ addSipMessageResponse(SipMessageResponse stats)443 public synchronized void addSipMessageResponse(SipMessageResponse stats) { 444 SipMessageResponse existingStats = find(stats); 445 if (existingStats != null) { 446 existingStats.count += 1; 447 } else { 448 mAtoms.sipMessageResponse = insertAtRandomPlace(mAtoms.sipMessageResponse, stats, 449 MAX_NUM_SIP_MESSAGE_RESPONSE_STATS); 450 } 451 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 452 } 453 454 /** Adds a new {@link SipTransportSession} to the storage. */ addCompleteSipTransportSession(SipTransportSession stats)455 public synchronized void addCompleteSipTransportSession(SipTransportSession stats) { 456 SipTransportSession existingStats = find(stats); 457 if (existingStats != null) { 458 existingStats.sessionCount += 1; 459 if (stats.isEndedGracefully) { 460 existingStats.endedGracefullyCount += 1; 461 } 462 } else { 463 mAtoms.sipTransportSession = 464 insertAtRandomPlace(mAtoms.sipTransportSession, stats, 465 MAX_NUM_SIP_TRANSPORT_SESSION_STATS); 466 } 467 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 468 } 469 470 /** Adds a new {@link ImsDedicatedBearerListenerEvent} to the storage. */ addImsDedicatedBearerListenerEvent( ImsDedicatedBearerListenerEvent stats)471 public synchronized void addImsDedicatedBearerListenerEvent( 472 ImsDedicatedBearerListenerEvent stats) { 473 ImsDedicatedBearerListenerEvent existingStats = find(stats); 474 if (existingStats != null) { 475 existingStats.eventCount += 1; 476 } else { 477 mAtoms.imsDedicatedBearerListenerEvent = 478 insertAtRandomPlace(mAtoms.imsDedicatedBearerListenerEvent, 479 stats, MAX_NUM_DEDICATED_BEARER_LISTENER_EVENT_STATS); 480 } 481 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 482 } 483 484 /** Adds a new {@link ImsDedicatedBearerEvent} to the storage. */ addImsDedicatedBearerEvent(ImsDedicatedBearerEvent stats)485 public synchronized void addImsDedicatedBearerEvent(ImsDedicatedBearerEvent stats) { 486 ImsDedicatedBearerEvent existingStats = find(stats); 487 if (existingStats != null) { 488 existingStats.count += 1; 489 } else { 490 mAtoms.imsDedicatedBearerEvent = 491 insertAtRandomPlace(mAtoms.imsDedicatedBearerEvent, stats, 492 MAX_NUM_DEDICATED_BEARER_EVENT_STATS); 493 } 494 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 495 } 496 497 /** Adds a new {@link ImsRegistrationServiceDescStats} to the storage. */ addImsRegistrationServiceDescStats( ImsRegistrationServiceDescStats stats)498 public synchronized void addImsRegistrationServiceDescStats( 499 ImsRegistrationServiceDescStats stats) { 500 ImsRegistrationServiceDescStats existingStats = find(stats); 501 if (existingStats != null) { 502 existingStats.publishedMillis += stats.publishedMillis; 503 } else { 504 mAtoms.imsRegistrationServiceDescStats = 505 insertAtRandomPlace(mAtoms.imsRegistrationServiceDescStats, 506 stats, MAX_NUM_IMS_REGISTRATION_SERVICE_DESC_STATS); 507 } 508 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 509 } 510 511 /** Adds a new {@link UceEventStats} to the storage. */ addUceEventStats(UceEventStats stats)512 public synchronized void addUceEventStats(UceEventStats stats) { 513 UceEventStats existingStats = find(stats); 514 if (existingStats != null) { 515 existingStats.count += 1; 516 } else { 517 mAtoms.uceEventStats = 518 insertAtRandomPlace(mAtoms.uceEventStats, stats, MAX_NUM_UCE_EVENT_STATS); 519 } 520 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 521 } 522 523 /** Adds a new {@link PresenceNotifyEvent} to the storage. */ addPresenceNotifyEvent(PresenceNotifyEvent stats)524 public synchronized void addPresenceNotifyEvent(PresenceNotifyEvent stats) { 525 PresenceNotifyEvent existingStats = find(stats); 526 if (existingStats != null) { 527 existingStats.rcsCapsCount += stats.rcsCapsCount; 528 existingStats.mmtelCapsCount += stats.mmtelCapsCount; 529 existingStats.noCapsCount += stats.noCapsCount; 530 existingStats.count += stats.count; 531 } else { 532 mAtoms.presenceNotifyEvent = 533 insertAtRandomPlace(mAtoms.presenceNotifyEvent, stats, 534 MAX_NUM_PRESENCE_NOTIFY_EVENT_STATS); 535 } 536 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 537 } 538 539 /** Adds a new {@link GbaEvent} to the storage. */ addGbaEvent(GbaEvent stats)540 public synchronized void addGbaEvent(GbaEvent stats) { 541 GbaEvent existingStats = find(stats); 542 if (existingStats != null) { 543 existingStats.count += 1; 544 } else { 545 mAtoms.gbaEvent = 546 insertAtRandomPlace(mAtoms.gbaEvent, stats, MAX_NUM_GBA_EVENT_STATS); 547 } 548 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); 549 } 550 551 /** 552 * Returns and clears the voice call sessions if last pulled longer than {@code 553 * minIntervalMillis} ago, otherwise returns {@code null}. 554 */ 555 @Nullable getVoiceCallSessions(long minIntervalMillis)556 public synchronized VoiceCallSession[] getVoiceCallSessions(long minIntervalMillis) { 557 if (getWallTimeMillis() - mAtoms.voiceCallSessionPullTimestampMillis > minIntervalMillis) { 558 mAtoms.voiceCallSessionPullTimestampMillis = getWallTimeMillis(); 559 VoiceCallSession[] previousCalls = mAtoms.voiceCallSession; 560 mAtoms.voiceCallSession = new VoiceCallSession[0]; 561 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 562 return previousCalls; 563 } else { 564 return null; 565 } 566 } 567 568 /** 569 * Returns and clears the voice call RAT usages if last pulled longer than {@code 570 * minIntervalMillis} ago, otherwise returns {@code null}. 571 */ 572 @Nullable getVoiceCallRatUsages(long minIntervalMillis)573 public synchronized VoiceCallRatUsage[] getVoiceCallRatUsages(long minIntervalMillis) { 574 if (getWallTimeMillis() - mAtoms.voiceCallRatUsagePullTimestampMillis > minIntervalMillis) { 575 mAtoms.voiceCallRatUsagePullTimestampMillis = getWallTimeMillis(); 576 VoiceCallRatUsage[] previousUsages = mAtoms.voiceCallRatUsage; 577 mVoiceCallRatTracker.clear(); 578 mAtoms.voiceCallRatUsage = new VoiceCallRatUsage[0]; 579 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 580 return previousUsages; 581 } else { 582 return null; 583 } 584 } 585 586 /** 587 * Returns and clears the incoming SMS if last pulled longer than {@code minIntervalMillis} ago, 588 * otherwise returns {@code null}. 589 */ 590 @Nullable getIncomingSms(long minIntervalMillis)591 public synchronized IncomingSms[] getIncomingSms(long minIntervalMillis) { 592 if (getWallTimeMillis() - mAtoms.incomingSmsPullTimestampMillis > minIntervalMillis) { 593 mAtoms.incomingSmsPullTimestampMillis = getWallTimeMillis(); 594 IncomingSms[] previousIncomingSms = mAtoms.incomingSms; 595 mAtoms.incomingSms = new IncomingSms[0]; 596 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 597 return previousIncomingSms; 598 } else { 599 return null; 600 } 601 } 602 603 /** 604 * Returns and clears the outgoing SMS if last pulled longer than {@code minIntervalMillis} ago, 605 * otherwise returns {@code null}. 606 */ 607 @Nullable getOutgoingSms(long minIntervalMillis)608 public synchronized OutgoingSms[] getOutgoingSms(long minIntervalMillis) { 609 if (getWallTimeMillis() - mAtoms.outgoingSmsPullTimestampMillis > minIntervalMillis) { 610 mAtoms.outgoingSmsPullTimestampMillis = getWallTimeMillis(); 611 OutgoingSms[] previousOutgoingSms = mAtoms.outgoingSms; 612 mAtoms.outgoingSms = new OutgoingSms[0]; 613 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 614 return previousOutgoingSms; 615 } else { 616 return null; 617 } 618 } 619 620 /** 621 * Returns and clears the data call session if last pulled longer than {@code minIntervalMillis} 622 * ago, otherwise returns {@code null}. 623 */ 624 @Nullable getDataCallSessions(long minIntervalMillis)625 public synchronized DataCallSession[] getDataCallSessions(long minIntervalMillis) { 626 if (getWallTimeMillis() - mAtoms.dataCallSessionPullTimestampMillis > minIntervalMillis) { 627 mAtoms.dataCallSessionPullTimestampMillis = getWallTimeMillis(); 628 DataCallSession[] previousDataCallSession = mAtoms.dataCallSession; 629 mAtoms.dataCallSession = new DataCallSession[0]; 630 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 631 return previousDataCallSession; 632 } else { 633 return null; 634 } 635 } 636 637 /** 638 * Returns and clears the service state durations if last pulled longer than {@code 639 * minIntervalMillis} ago, otherwise returns {@code null}. 640 */ 641 @Nullable getCellularServiceStates(long minIntervalMillis)642 public synchronized CellularServiceState[] getCellularServiceStates(long minIntervalMillis) { 643 if (getWallTimeMillis() - mAtoms.cellularServiceStatePullTimestampMillis 644 > minIntervalMillis) { 645 mAtoms.cellularServiceStatePullTimestampMillis = getWallTimeMillis(); 646 CellularServiceState[] previousStates = mAtoms.cellularServiceState; 647 Arrays.stream(previousStates).forEach(state -> state.lastUsedMillis = 0L); 648 mAtoms.cellularServiceState = new CellularServiceState[0]; 649 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 650 return previousStates; 651 } else { 652 return null; 653 } 654 } 655 656 /** 657 * Returns and clears the service state durations if last pulled longer than {@code 658 * minIntervalMillis} ago, otherwise returns {@code null}. 659 */ 660 @Nullable getCellularDataServiceSwitches( long minIntervalMillis)661 public synchronized CellularDataServiceSwitch[] getCellularDataServiceSwitches( 662 long minIntervalMillis) { 663 if (getWallTimeMillis() - mAtoms.cellularDataServiceSwitchPullTimestampMillis 664 > minIntervalMillis) { 665 mAtoms.cellularDataServiceSwitchPullTimestampMillis = getWallTimeMillis(); 666 CellularDataServiceSwitch[] previousSwitches = mAtoms.cellularDataServiceSwitch; 667 Arrays.stream(previousSwitches) 668 .forEach(serviceSwitch -> serviceSwitch.lastUsedMillis = 0L); 669 mAtoms.cellularDataServiceSwitch = new CellularDataServiceSwitch[0]; 670 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 671 return previousSwitches; 672 } else { 673 return null; 674 } 675 } 676 677 /** 678 * Returns and clears the IMS registration statistics if last pulled longer than {@code 679 * minIntervalMillis} ago, otherwise returns {@code null}. 680 */ 681 @Nullable getImsRegistrationStats(long minIntervalMillis)682 public synchronized ImsRegistrationStats[] getImsRegistrationStats(long minIntervalMillis) { 683 if (getWallTimeMillis() - mAtoms.imsRegistrationStatsPullTimestampMillis 684 > minIntervalMillis) { 685 mAtoms.imsRegistrationStatsPullTimestampMillis = getWallTimeMillis(); 686 ImsRegistrationStats[] previousStats = mAtoms.imsRegistrationStats; 687 Arrays.stream(previousStats).forEach(stats -> stats.lastUsedMillis = 0L); 688 mAtoms.imsRegistrationStats = new ImsRegistrationStats[0]; 689 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 690 return previousStats; 691 } else { 692 return null; 693 } 694 } 695 696 /** 697 * Returns and clears the IMS registration terminations if last pulled longer than {@code 698 * minIntervalMillis} ago, otherwise returns {@code null}. 699 */ 700 @Nullable getImsRegistrationTerminations( long minIntervalMillis)701 public synchronized ImsRegistrationTermination[] getImsRegistrationTerminations( 702 long minIntervalMillis) { 703 if (getWallTimeMillis() - mAtoms.imsRegistrationTerminationPullTimestampMillis 704 > minIntervalMillis) { 705 mAtoms.imsRegistrationTerminationPullTimestampMillis = getWallTimeMillis(); 706 ImsRegistrationTermination[] previousTerminations = mAtoms.imsRegistrationTermination; 707 Arrays.stream(previousTerminations) 708 .forEach(termination -> termination.lastUsedMillis = 0L); 709 mAtoms.imsRegistrationTermination = new ImsRegistrationTermination[0]; 710 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 711 return previousTerminations; 712 } else { 713 return null; 714 } 715 } 716 717 /** 718 * Returns and clears the network requests if last pulled longer than {@code 719 * minIntervalMillis} ago, otherwise returns {@code null}. 720 */ 721 @Nullable getNetworkRequests(long minIntervalMillis)722 public synchronized NetworkRequests[] getNetworkRequests(long minIntervalMillis) { 723 if (getWallTimeMillis() - mAtoms.networkRequestsPullTimestampMillis > minIntervalMillis) { 724 mAtoms.networkRequestsPullTimestampMillis = getWallTimeMillis(); 725 NetworkRequests[] previousNetworkRequests = mAtoms.networkRequests; 726 mAtoms.networkRequests = new NetworkRequests[0]; 727 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 728 return previousNetworkRequests; 729 } else { 730 return null; 731 } 732 } 733 734 /** 735 * Returns and clears the ImsRegistrationFeatureTagStats if last pulled longer than {@code 736 * minIntervalMillis} ago, otherwise returns {@code null}. 737 */ 738 @Nullable getImsRegistrationFeatureTagStats( long minIntervalMillis)739 public synchronized ImsRegistrationFeatureTagStats[] getImsRegistrationFeatureTagStats( 740 long minIntervalMillis) { 741 if (getWallTimeMillis() - mAtoms.imsRegistrationFeatureTagStatsPullTimestampMillis 742 > minIntervalMillis) { 743 mAtoms.imsRegistrationFeatureTagStatsPullTimestampMillis = getWallTimeMillis(); 744 ImsRegistrationFeatureTagStats[] previousStats = 745 mAtoms.imsRegistrationFeatureTagStats; 746 mAtoms.imsRegistrationFeatureTagStats = new ImsRegistrationFeatureTagStats[0]; 747 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 748 return previousStats; 749 } else { 750 return null; 751 } 752 } 753 754 /** 755 * Returns and clears the RcsClientProvisioningStats if last pulled longer than {@code 756 * minIntervalMillis} ago, otherwise returns {@code null}. 757 */ 758 @Nullable getRcsClientProvisioningStats( long minIntervalMillis)759 public synchronized RcsClientProvisioningStats[] getRcsClientProvisioningStats( 760 long minIntervalMillis) { 761 if (getWallTimeMillis() - mAtoms.rcsClientProvisioningStatsPullTimestampMillis 762 > minIntervalMillis) { 763 mAtoms.rcsClientProvisioningStatsPullTimestampMillis = getWallTimeMillis(); 764 RcsClientProvisioningStats[] previousStats = mAtoms.rcsClientProvisioningStats; 765 mAtoms.rcsClientProvisioningStats = new RcsClientProvisioningStats[0]; 766 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 767 return previousStats; 768 } else { 769 return null; 770 } 771 } 772 773 /** 774 * Returns and clears the RcsAcsProvisioningStats if last pulled longer than {@code 775 * minIntervalMillis} ago, otherwise returns {@code null}. 776 */ 777 @Nullable getRcsAcsProvisioningStats( long minIntervalMillis)778 public synchronized RcsAcsProvisioningStats[] getRcsAcsProvisioningStats( 779 long minIntervalMillis) { 780 if (getWallTimeMillis() - mAtoms.rcsAcsProvisioningStatsPullTimestampMillis 781 > minIntervalMillis) { 782 mAtoms.rcsAcsProvisioningStatsPullTimestampMillis = getWallTimeMillis(); 783 RcsAcsProvisioningStats[] previousStats = mAtoms.rcsAcsProvisioningStats; 784 mAtoms.rcsAcsProvisioningStats = new RcsAcsProvisioningStats[0]; 785 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 786 return previousStats; 787 } else { 788 return null; 789 } 790 } 791 792 /** 793 * Returns and clears the SipDelegateStats if last pulled longer than {@code 794 * minIntervalMillis} ago, otherwise returns {@code null}. 795 */ 796 @Nullable getSipDelegateStats(long minIntervalMillis)797 public synchronized SipDelegateStats[] getSipDelegateStats(long minIntervalMillis) { 798 if (getWallTimeMillis() - mAtoms.sipDelegateStatsPullTimestampMillis 799 > minIntervalMillis) { 800 mAtoms.sipDelegateStatsPullTimestampMillis = getWallTimeMillis(); 801 SipDelegateStats[] previousStats = mAtoms.sipDelegateStats; 802 mAtoms.sipDelegateStats = new SipDelegateStats[0]; 803 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 804 return previousStats; 805 } else { 806 return null; 807 } 808 } 809 810 /** 811 * Returns and clears the SipTransportFeatureTagStats if last pulled longer than {@code 812 * minIntervalMillis} ago, otherwise returns {@code null}. 813 */ 814 @Nullable getSipTransportFeatureTagStats( long minIntervalMillis)815 public synchronized SipTransportFeatureTagStats[] getSipTransportFeatureTagStats( 816 long minIntervalMillis) { 817 if (getWallTimeMillis() - mAtoms.sipTransportFeatureTagStatsPullTimestampMillis 818 > minIntervalMillis) { 819 mAtoms.sipTransportFeatureTagStatsPullTimestampMillis = getWallTimeMillis(); 820 SipTransportFeatureTagStats[] previousStats = mAtoms.sipTransportFeatureTagStats; 821 mAtoms.sipTransportFeatureTagStats = new SipTransportFeatureTagStats[0]; 822 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 823 return previousStats; 824 } else { 825 return null; 826 } 827 } 828 829 /** 830 * Returns and clears the SipMessageResponse if last pulled longer than {@code 831 * minIntervalMillis} ago, otherwise returns {@code null}. 832 */ 833 @Nullable getSipMessageResponse(long minIntervalMillis)834 public synchronized SipMessageResponse[] getSipMessageResponse(long minIntervalMillis) { 835 if (getWallTimeMillis() - mAtoms.sipMessageResponsePullTimestampMillis 836 > minIntervalMillis) { 837 mAtoms.sipMessageResponsePullTimestampMillis = getWallTimeMillis(); 838 SipMessageResponse[] previousStats = 839 mAtoms.sipMessageResponse; 840 mAtoms.sipMessageResponse = new SipMessageResponse[0]; 841 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 842 return previousStats; 843 } else { 844 return null; 845 } 846 } 847 848 /** 849 * Returns and clears the SipTransportSession if last pulled longer than {@code 850 * minIntervalMillis} ago, otherwise returns {@code null}. 851 */ 852 @Nullable getSipTransportSession(long minIntervalMillis)853 public synchronized SipTransportSession[] getSipTransportSession(long minIntervalMillis) { 854 if (getWallTimeMillis() - mAtoms.sipTransportSessionPullTimestampMillis 855 > minIntervalMillis) { 856 mAtoms.sipTransportSessionPullTimestampMillis = getWallTimeMillis(); 857 SipTransportSession[] previousStats = 858 mAtoms.sipTransportSession; 859 mAtoms.sipTransportSession = new SipTransportSession[0]; 860 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 861 return previousStats; 862 } else { 863 return null; 864 } 865 } 866 867 /** 868 * Returns and clears the ImsDedicatedBearerListenerEvent if last pulled longer than {@code 869 * minIntervalMillis} ago, otherwise returns {@code null}. 870 */ 871 @Nullable getImsDedicatedBearerListenerEvent( long minIntervalMillis)872 public synchronized ImsDedicatedBearerListenerEvent[] getImsDedicatedBearerListenerEvent( 873 long minIntervalMillis) { 874 if (getWallTimeMillis() - mAtoms.imsDedicatedBearerListenerEventPullTimestampMillis 875 > minIntervalMillis) { 876 mAtoms.imsDedicatedBearerListenerEventPullTimestampMillis = getWallTimeMillis(); 877 ImsDedicatedBearerListenerEvent[] previousStats = 878 mAtoms.imsDedicatedBearerListenerEvent; 879 mAtoms.imsDedicatedBearerListenerEvent = new ImsDedicatedBearerListenerEvent[0]; 880 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 881 return previousStats; 882 } else { 883 return null; 884 } 885 } 886 887 /** 888 * Returns and clears the ImsDedicatedBearerEvent if last pulled longer than {@code 889 * minIntervalMillis} ago, otherwise returns {@code null}. 890 */ 891 @Nullable getImsDedicatedBearerEvent( long minIntervalMillis)892 public synchronized ImsDedicatedBearerEvent[] getImsDedicatedBearerEvent( 893 long minIntervalMillis) { 894 if (getWallTimeMillis() - mAtoms.imsDedicatedBearerEventPullTimestampMillis 895 > minIntervalMillis) { 896 mAtoms.imsDedicatedBearerEventPullTimestampMillis = getWallTimeMillis(); 897 ImsDedicatedBearerEvent[] previousStats = 898 mAtoms.imsDedicatedBearerEvent; 899 mAtoms.imsDedicatedBearerEvent = new ImsDedicatedBearerEvent[0]; 900 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 901 return previousStats; 902 } else { 903 return null; 904 } 905 } 906 907 /** 908 * Returns and clears the ImsRegistrationServiceDescStats if last pulled longer than {@code 909 * minIntervalMillis} ago, otherwise returns {@code null}. 910 */ 911 @Nullable getImsRegistrationServiceDescStats(long minIntervalMillis)912 public synchronized ImsRegistrationServiceDescStats[] getImsRegistrationServiceDescStats(long 913 minIntervalMillis) { 914 if (getWallTimeMillis() - mAtoms.imsRegistrationServiceDescStatsPullTimestampMillis 915 > minIntervalMillis) { 916 mAtoms.imsRegistrationServiceDescStatsPullTimestampMillis = getWallTimeMillis(); 917 ImsRegistrationServiceDescStats[] previousStats = 918 mAtoms.imsRegistrationServiceDescStats; 919 mAtoms.imsRegistrationServiceDescStats = new ImsRegistrationServiceDescStats[0]; 920 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 921 return previousStats; 922 } else { 923 return null; 924 } 925 } 926 927 /** 928 * Returns and clears the UceEventStats if last pulled longer than {@code 929 * minIntervalMillis} ago, otherwise returns {@code null}. 930 */ 931 @Nullable getUceEventStats(long minIntervalMillis)932 public synchronized UceEventStats[] getUceEventStats(long minIntervalMillis) { 933 if (getWallTimeMillis() - mAtoms.uceEventStatsPullTimestampMillis > minIntervalMillis) { 934 mAtoms.uceEventStatsPullTimestampMillis = getWallTimeMillis(); 935 UceEventStats[] previousStats = mAtoms.uceEventStats; 936 mAtoms.uceEventStats = new UceEventStats[0]; 937 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 938 return previousStats; 939 } else { 940 return null; 941 } 942 } 943 944 /** 945 * Returns and clears the PresenceNotifyEvent if last pulled longer than {@code 946 * minIntervalMillis} ago, otherwise returns {@code null}. 947 */ 948 @Nullable getPresenceNotifyEvent(long minIntervalMillis)949 public synchronized PresenceNotifyEvent[] getPresenceNotifyEvent(long minIntervalMillis) { 950 if (getWallTimeMillis() - mAtoms.presenceNotifyEventPullTimestampMillis 951 > minIntervalMillis) { 952 mAtoms.presenceNotifyEventPullTimestampMillis = getWallTimeMillis(); 953 PresenceNotifyEvent[] previousStats = mAtoms.presenceNotifyEvent; 954 mAtoms.presenceNotifyEvent = new PresenceNotifyEvent[0]; 955 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 956 return previousStats; 957 } else { 958 return null; 959 } 960 } 961 962 /** 963 * Returns and clears the GbaEvent if last pulled longer than {@code 964 * minIntervalMillis} ago, otherwise returns {@code null}. 965 */ 966 @Nullable getGbaEvent(long minIntervalMillis)967 public synchronized GbaEvent[] getGbaEvent(long minIntervalMillis) { 968 if (getWallTimeMillis() - mAtoms.gbaEventPullTimestampMillis > minIntervalMillis) { 969 mAtoms.gbaEventPullTimestampMillis = getWallTimeMillis(); 970 GbaEvent[] previousStats = mAtoms.gbaEvent; 971 mAtoms.gbaEvent = new GbaEvent[0]; 972 saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS); 973 return previousStats; 974 } else { 975 return null; 976 } 977 } 978 979 /** Loads {@link PersistAtoms} from a file in private storage. */ loadAtomsFromFile()980 private PersistAtoms loadAtomsFromFile() { 981 try { 982 PersistAtoms atoms = 983 PersistAtoms.parseFrom( 984 Files.readAllBytes(mContext.getFileStreamPath(FILENAME).toPath())); 985 // Start from scratch if build changes, since mixing atoms from different builds could 986 // produce strange results 987 if (!Build.FINGERPRINT.equals(atoms.buildFingerprint)) { 988 Rlog.d(TAG, "Build changed"); 989 return makeNewPersistAtoms(); 990 } 991 // check all the fields in case of situations such as OTA or crash during saving 992 atoms.voiceCallRatUsage = 993 sanitizeAtoms(atoms.voiceCallRatUsage, VoiceCallRatUsage.class); 994 atoms.voiceCallSession = 995 sanitizeAtoms( 996 atoms.voiceCallSession, VoiceCallSession.class, MAX_NUM_CALL_SESSIONS); 997 atoms.incomingSms = sanitizeAtoms(atoms.incomingSms, IncomingSms.class, MAX_NUM_SMS); 998 atoms.outgoingSms = sanitizeAtoms(atoms.outgoingSms, OutgoingSms.class, MAX_NUM_SMS); 999 atoms.carrierIdMismatch = 1000 sanitizeAtoms( 1001 atoms.carrierIdMismatch, 1002 CarrierIdMismatch.class, 1003 MAX_CARRIER_ID_MISMATCH); 1004 atoms.dataCallSession = 1005 sanitizeAtoms( 1006 atoms.dataCallSession, 1007 DataCallSession.class, 1008 MAX_NUM_DATA_CALL_SESSIONS); 1009 atoms.cellularServiceState = 1010 sanitizeAtoms( 1011 atoms.cellularServiceState, 1012 CellularServiceState.class, 1013 MAX_NUM_CELLULAR_SERVICE_STATES); 1014 atoms.cellularDataServiceSwitch = 1015 sanitizeAtoms( 1016 atoms.cellularDataServiceSwitch, 1017 CellularDataServiceSwitch.class, 1018 MAX_NUM_CELLULAR_DATA_SERVICE_SWITCHES); 1019 atoms.imsRegistrationStats = 1020 sanitizeAtoms( 1021 atoms.imsRegistrationStats, 1022 ImsRegistrationStats.class, 1023 MAX_NUM_IMS_REGISTRATION_STATS); 1024 atoms.imsRegistrationTermination = 1025 sanitizeAtoms( 1026 atoms.imsRegistrationTermination, 1027 ImsRegistrationTermination.class, 1028 MAX_NUM_IMS_REGISTRATION_TERMINATIONS); 1029 atoms.networkRequests = sanitizeAtoms(atoms.networkRequests, NetworkRequests.class); 1030 atoms.imsRegistrationFeatureTagStats = 1031 sanitizeAtoms( 1032 atoms.imsRegistrationFeatureTagStats, 1033 ImsRegistrationFeatureTagStats.class, 1034 MAX_NUM_IMS_REGISTRATION_FEATURE_STATS); 1035 atoms.rcsClientProvisioningStats = 1036 sanitizeAtoms( 1037 atoms.rcsClientProvisioningStats, 1038 RcsClientProvisioningStats.class, 1039 MAX_NUM_RCS_CLIENT_PROVISIONING_STATS); 1040 atoms.rcsAcsProvisioningStats = 1041 sanitizeAtoms( 1042 atoms.rcsAcsProvisioningStats, 1043 RcsAcsProvisioningStats.class, 1044 MAX_NUM_RCS_ACS_PROVISIONING_STATS); 1045 atoms.sipDelegateStats = 1046 sanitizeAtoms( 1047 atoms.sipDelegateStats, 1048 SipDelegateStats.class, 1049 MAX_NUM_SIP_DELEGATE_STATS); 1050 atoms.sipTransportFeatureTagStats = 1051 sanitizeAtoms( 1052 atoms.sipTransportFeatureTagStats, 1053 SipTransportFeatureTagStats.class, 1054 MAX_NUM_SIP_TRANSPORT_FEATURE_TAG_STATS); 1055 atoms.sipMessageResponse = 1056 sanitizeAtoms( 1057 atoms.sipMessageResponse, 1058 SipMessageResponse.class, 1059 MAX_NUM_SIP_MESSAGE_RESPONSE_STATS); 1060 atoms.sipTransportSession = 1061 sanitizeAtoms( 1062 atoms.sipTransportSession, 1063 SipTransportSession.class, 1064 MAX_NUM_SIP_TRANSPORT_SESSION_STATS); 1065 atoms.imsDedicatedBearerListenerEvent = 1066 sanitizeAtoms( 1067 atoms.imsDedicatedBearerListenerEvent, 1068 ImsDedicatedBearerListenerEvent.class, 1069 MAX_NUM_DEDICATED_BEARER_LISTENER_EVENT_STATS); 1070 atoms.imsDedicatedBearerEvent = 1071 sanitizeAtoms( 1072 atoms.imsDedicatedBearerEvent, 1073 ImsDedicatedBearerEvent.class, 1074 MAX_NUM_DEDICATED_BEARER_EVENT_STATS); 1075 atoms.imsRegistrationServiceDescStats = 1076 sanitizeAtoms( 1077 atoms.imsRegistrationServiceDescStats, 1078 ImsRegistrationServiceDescStats.class, 1079 MAX_NUM_IMS_REGISTRATION_SERVICE_DESC_STATS); 1080 atoms.uceEventStats = 1081 sanitizeAtoms( 1082 atoms.uceEventStats, 1083 UceEventStats.class, 1084 MAX_NUM_UCE_EVENT_STATS); 1085 atoms.presenceNotifyEvent = 1086 sanitizeAtoms( 1087 atoms.presenceNotifyEvent, 1088 PresenceNotifyEvent.class, 1089 MAX_NUM_PRESENCE_NOTIFY_EVENT_STATS); 1090 atoms.gbaEvent = 1091 sanitizeAtoms( 1092 atoms.gbaEvent, 1093 GbaEvent.class, 1094 MAX_NUM_GBA_EVENT_STATS); 1095 1096 // out of caution, sanitize also the timestamps 1097 atoms.voiceCallRatUsagePullTimestampMillis = 1098 sanitizeTimestamp(atoms.voiceCallRatUsagePullTimestampMillis); 1099 atoms.voiceCallSessionPullTimestampMillis = 1100 sanitizeTimestamp(atoms.voiceCallSessionPullTimestampMillis); 1101 atoms.incomingSmsPullTimestampMillis = 1102 sanitizeTimestamp(atoms.incomingSmsPullTimestampMillis); 1103 atoms.outgoingSmsPullTimestampMillis = 1104 sanitizeTimestamp(atoms.outgoingSmsPullTimestampMillis); 1105 atoms.dataCallSessionPullTimestampMillis = 1106 sanitizeTimestamp(atoms.dataCallSessionPullTimestampMillis); 1107 atoms.cellularServiceStatePullTimestampMillis = 1108 sanitizeTimestamp(atoms.cellularServiceStatePullTimestampMillis); 1109 atoms.cellularDataServiceSwitchPullTimestampMillis = 1110 sanitizeTimestamp(atoms.cellularDataServiceSwitchPullTimestampMillis); 1111 atoms.imsRegistrationStatsPullTimestampMillis = 1112 sanitizeTimestamp(atoms.imsRegistrationStatsPullTimestampMillis); 1113 atoms.imsRegistrationTerminationPullTimestampMillis = 1114 sanitizeTimestamp(atoms.imsRegistrationTerminationPullTimestampMillis); 1115 atoms.networkRequestsPullTimestampMillis = 1116 sanitizeTimestamp(atoms.networkRequestsPullTimestampMillis); 1117 atoms.imsRegistrationFeatureTagStatsPullTimestampMillis = 1118 sanitizeTimestamp(atoms.imsRegistrationFeatureTagStatsPullTimestampMillis); 1119 atoms.rcsClientProvisioningStatsPullTimestampMillis = 1120 sanitizeTimestamp(atoms.rcsClientProvisioningStatsPullTimestampMillis); 1121 atoms.rcsAcsProvisioningStatsPullTimestampMillis = 1122 sanitizeTimestamp(atoms.rcsAcsProvisioningStatsPullTimestampMillis); 1123 atoms.sipDelegateStatsPullTimestampMillis = 1124 sanitizeTimestamp(atoms.sipDelegateStatsPullTimestampMillis); 1125 atoms.sipTransportFeatureTagStatsPullTimestampMillis = 1126 sanitizeTimestamp(atoms.sipTransportFeatureTagStatsPullTimestampMillis); 1127 atoms.sipMessageResponsePullTimestampMillis = 1128 sanitizeTimestamp(atoms.sipMessageResponsePullTimestampMillis); 1129 atoms.sipTransportSessionPullTimestampMillis = 1130 sanitizeTimestamp(atoms.sipTransportSessionPullTimestampMillis); 1131 atoms.imsDedicatedBearerListenerEventPullTimestampMillis = 1132 sanitizeTimestamp(atoms.imsDedicatedBearerListenerEventPullTimestampMillis); 1133 atoms.imsDedicatedBearerEventPullTimestampMillis = 1134 sanitizeTimestamp(atoms.imsDedicatedBearerEventPullTimestampMillis); 1135 atoms.imsRegistrationServiceDescStatsPullTimestampMillis = 1136 sanitizeTimestamp(atoms.imsRegistrationServiceDescStatsPullTimestampMillis); 1137 atoms.uceEventStatsPullTimestampMillis = 1138 sanitizeTimestamp(atoms.uceEventStatsPullTimestampMillis); 1139 atoms.presenceNotifyEventPullTimestampMillis = 1140 sanitizeTimestamp(atoms.presenceNotifyEventPullTimestampMillis); 1141 atoms.gbaEventPullTimestampMillis = 1142 sanitizeTimestamp(atoms.gbaEventPullTimestampMillis); 1143 1144 return atoms; 1145 } catch (NoSuchFileException e) { 1146 Rlog.d(TAG, "PersistAtoms file not found"); 1147 } catch (IOException | NullPointerException e) { 1148 Rlog.e(TAG, "cannot load/parse PersistAtoms", e); 1149 } 1150 return makeNewPersistAtoms(); 1151 } 1152 1153 /** 1154 * Posts message to save a copy of {@link PersistAtoms} to a file after a delay. 1155 * 1156 * <p>The delay is introduced to avoid too frequent operations to disk, which would negatively 1157 * impact the power consumption. 1158 */ saveAtomsToFile(int delayMillis)1159 private void saveAtomsToFile(int delayMillis) { 1160 if (delayMillis > 0 && !mSaveImmediately) { 1161 mHandler.removeCallbacks(mSaveRunnable); 1162 if (mHandler.postDelayed(mSaveRunnable, delayMillis)) { 1163 return; 1164 } 1165 } 1166 // In case of error posting the event or if delay is 0, save immediately 1167 saveAtomsToFileNow(); 1168 } 1169 1170 /** Saves a copy of {@link PersistAtoms} to a file in private storage. */ saveAtomsToFileNow()1171 private synchronized void saveAtomsToFileNow() { 1172 try (FileOutputStream stream = mContext.openFileOutput(FILENAME, Context.MODE_PRIVATE)) { 1173 stream.write(PersistAtoms.toByteArray(mAtoms)); 1174 } catch (IOException e) { 1175 Rlog.e(TAG, "cannot save PersistAtoms", e); 1176 } 1177 } 1178 1179 /** 1180 * Returns the service state that has the same dimension values with the given one, or {@code 1181 * null} if it does not exist. 1182 */ find(CellularServiceState key)1183 private @Nullable CellularServiceState find(CellularServiceState key) { 1184 for (CellularServiceState state : mAtoms.cellularServiceState) { 1185 if (state.voiceRat == key.voiceRat 1186 && state.dataRat == key.dataRat 1187 && state.voiceRoamingType == key.voiceRoamingType 1188 && state.dataRoamingType == key.dataRoamingType 1189 && state.isEndc == key.isEndc 1190 && state.simSlotIndex == key.simSlotIndex 1191 && state.isMultiSim == key.isMultiSim 1192 && state.carrierId == key.carrierId) { 1193 return state; 1194 } 1195 } 1196 return null; 1197 } 1198 1199 /** 1200 * Returns the data service switch that has the same dimension values with the given one, or 1201 * {@code null} if it does not exist. 1202 */ find(CellularDataServiceSwitch key)1203 private @Nullable CellularDataServiceSwitch find(CellularDataServiceSwitch key) { 1204 for (CellularDataServiceSwitch serviceSwitch : mAtoms.cellularDataServiceSwitch) { 1205 if (serviceSwitch.ratFrom == key.ratFrom 1206 && serviceSwitch.ratTo == key.ratTo 1207 && serviceSwitch.simSlotIndex == key.simSlotIndex 1208 && serviceSwitch.isMultiSim == key.isMultiSim 1209 && serviceSwitch.carrierId == key.carrierId) { 1210 return serviceSwitch; 1211 } 1212 } 1213 return null; 1214 } 1215 1216 /** 1217 * Returns the carrier ID mismatch event that has the same dimension values with the given one, 1218 * or {@code null} if it does not exist. 1219 */ find(CarrierIdMismatch key)1220 private @Nullable CarrierIdMismatch find(CarrierIdMismatch key) { 1221 for (CarrierIdMismatch mismatch : mAtoms.carrierIdMismatch) { 1222 if (mismatch.mccMnc.equals(key.mccMnc) 1223 && mismatch.gid1.equals(key.gid1) 1224 && mismatch.spn.equals(key.spn) 1225 && mismatch.pnn.equals(key.pnn)) { 1226 return mismatch; 1227 } 1228 } 1229 return null; 1230 } 1231 1232 /** 1233 * Returns the IMS registration stats that has the same dimension values with the given one, or 1234 * {@code null} if it does not exist. 1235 */ find(ImsRegistrationStats key)1236 private @Nullable ImsRegistrationStats find(ImsRegistrationStats key) { 1237 for (ImsRegistrationStats stats : mAtoms.imsRegistrationStats) { 1238 if (stats.carrierId == key.carrierId 1239 && stats.simSlotIndex == key.simSlotIndex 1240 && stats.rat == key.rat) { 1241 return stats; 1242 } 1243 } 1244 return null; 1245 } 1246 1247 /** 1248 * Returns the IMS registration termination that has the same dimension values with the given 1249 * one, or {@code null} if it does not exist. 1250 */ find(ImsRegistrationTermination key)1251 private @Nullable ImsRegistrationTermination find(ImsRegistrationTermination key) { 1252 for (ImsRegistrationTermination termination : mAtoms.imsRegistrationTermination) { 1253 if (termination.carrierId == key.carrierId 1254 && termination.isMultiSim == key.isMultiSim 1255 && termination.ratAtEnd == key.ratAtEnd 1256 && termination.setupFailed == key.setupFailed 1257 && termination.reasonCode == key.reasonCode 1258 && termination.extraCode == key.extraCode 1259 && termination.extraMessage.equals(key.extraMessage)) { 1260 return termination; 1261 } 1262 } 1263 return null; 1264 } 1265 1266 /** 1267 * Returns the network requests event that has the same carrier id as the given one, 1268 * or {@code null} if it does not exist. 1269 */ find(NetworkRequests key)1270 private @Nullable NetworkRequests find(NetworkRequests key) { 1271 for (NetworkRequests item : mAtoms.networkRequests) { 1272 if (item.carrierId == key.carrierId) { 1273 return item; 1274 } 1275 } 1276 return null; 1277 } 1278 1279 /** 1280 * Returns the index of data call session that has the same random dimension as the given one, 1281 * or -1 if it does not exist. 1282 */ findIndex(DataCallSession key)1283 private int findIndex(DataCallSession key) { 1284 for (int i = 0; i < mAtoms.dataCallSession.length; i++) { 1285 if (mAtoms.dataCallSession[i].dimension == key.dimension) { 1286 return i; 1287 } 1288 } 1289 return -1; 1290 } 1291 /** 1292 * Returns the Dedicated Bearer Listener event that has the same carrier id, slot id, rat, qci 1293 * and established state as the given one, or {@code null} if it does not exist. 1294 */ find(ImsDedicatedBearerListenerEvent key)1295 private @Nullable ImsDedicatedBearerListenerEvent find(ImsDedicatedBearerListenerEvent key) { 1296 for (ImsDedicatedBearerListenerEvent stats : mAtoms.imsDedicatedBearerListenerEvent) { 1297 if (stats.carrierId == key.carrierId 1298 && stats.slotId == key.slotId 1299 && stats.ratAtEnd == key.ratAtEnd 1300 && stats.qci == key.qci 1301 && stats.dedicatedBearerEstablished == key.dedicatedBearerEstablished) { 1302 return stats; 1303 } 1304 } 1305 return null; 1306 } 1307 1308 /** 1309 * Returns the Dedicated Bearer event that has the same carrier id, slot id, rat, 1310 * qci, bearer state, local/remote connection and exsting listener as the given one, 1311 * or {@code null} if it does not exist. 1312 */ find(ImsDedicatedBearerEvent key)1313 private @Nullable ImsDedicatedBearerEvent find(ImsDedicatedBearerEvent key) { 1314 for (ImsDedicatedBearerEvent stats : mAtoms.imsDedicatedBearerEvent) { 1315 if (stats.carrierId == key.carrierId 1316 && stats.slotId == key.slotId 1317 && stats.ratAtEnd == key.ratAtEnd 1318 && stats.qci == key.qci 1319 && stats.bearerState == key.bearerState 1320 && stats.localConnectionInfoReceived == key.localConnectionInfoReceived 1321 && stats.remoteConnectionInfoReceived == key.remoteConnectionInfoReceived 1322 && stats.hasListeners == key.hasListeners) { 1323 return stats; 1324 } 1325 } 1326 return null; 1327 } 1328 1329 /** 1330 * Returns the Registration Feature Tag that has the same carrier id, slot id, 1331 * feature tag name or custom feature tag name and registration tech as the given one, 1332 * or {@code null} if it does not exist. 1333 */ find(ImsRegistrationFeatureTagStats key)1334 private @Nullable ImsRegistrationFeatureTagStats find(ImsRegistrationFeatureTagStats key) { 1335 for (ImsRegistrationFeatureTagStats stats : mAtoms.imsRegistrationFeatureTagStats) { 1336 if (stats.carrierId == key.carrierId 1337 && stats.slotId == key.slotId 1338 && stats.featureTagName == key.featureTagName 1339 && stats.registrationTech == key.registrationTech) { 1340 return stats; 1341 } 1342 } 1343 return null; 1344 } 1345 1346 /** 1347 * Returns Client Provisioning that has the same carrier id, slot id and event as the given 1348 * one, or {@code null} if it does not exist. 1349 */ find(RcsClientProvisioningStats key)1350 private @Nullable RcsClientProvisioningStats find(RcsClientProvisioningStats key) { 1351 for (RcsClientProvisioningStats stats : mAtoms.rcsClientProvisioningStats) { 1352 if (stats.carrierId == key.carrierId 1353 && stats.slotId == key.slotId 1354 && stats.event == key.event) { 1355 return stats; 1356 } 1357 } 1358 return null; 1359 } 1360 1361 /** 1362 * Returns ACS Provisioning that has the same carrier id, slot id, response code, response type 1363 * and SR supported as the given one, or {@code null} if it does not exist. 1364 */ find(RcsAcsProvisioningStats key)1365 private @Nullable RcsAcsProvisioningStats find(RcsAcsProvisioningStats key) { 1366 for (RcsAcsProvisioningStats stats : mAtoms.rcsAcsProvisioningStats) { 1367 if (stats.carrierId == key.carrierId 1368 && stats.slotId == key.slotId 1369 && stats.responseCode == key.responseCode 1370 && stats.responseType == key.responseType 1371 && stats.isSingleRegistrationEnabled == key.isSingleRegistrationEnabled) { 1372 return stats; 1373 } 1374 } 1375 return null; 1376 } 1377 1378 /** 1379 * Returns Sip Message Response that has the same carrier id, slot id, method, response, 1380 * direction and error as the given one, or {@code null} if it does not exist. 1381 */ find(SipMessageResponse key)1382 private @Nullable SipMessageResponse find(SipMessageResponse key) { 1383 for (SipMessageResponse stats : mAtoms.sipMessageResponse) { 1384 if (stats.carrierId == key.carrierId 1385 && stats.slotId == key.slotId 1386 && stats.sipMessageMethod == key.sipMessageMethod 1387 && stats.sipMessageResponse == key.sipMessageResponse 1388 && stats.sipMessageDirection == key.sipMessageDirection 1389 && stats.messageError == key.messageError) { 1390 return stats; 1391 } 1392 } 1393 return null; 1394 } 1395 1396 /** 1397 * Returns Sip Transport Session that has the same carrier id, slot id, method, direction and 1398 * response as the given one, or {@code null} if it does not exist. 1399 */ find(SipTransportSession key)1400 private @Nullable SipTransportSession find(SipTransportSession key) { 1401 for (SipTransportSession stats : mAtoms.sipTransportSession) { 1402 if (stats.carrierId == key.carrierId 1403 && stats.slotId == key.slotId 1404 && stats.sessionMethod == key.sessionMethod 1405 && stats.sipMessageDirection == key.sipMessageDirection 1406 && stats.sipResponse == key.sipResponse) { 1407 return stats; 1408 } 1409 } 1410 return null; 1411 } 1412 1413 /** 1414 * Returns Registration Service Desc Stats that has the same carrier id, slot id, service id or 1415 * custom service id, service id version and registration tech as the given one, 1416 * or {@code null} if it does not exist. 1417 */ find(ImsRegistrationServiceDescStats key)1418 private @Nullable ImsRegistrationServiceDescStats find(ImsRegistrationServiceDescStats key) { 1419 for (ImsRegistrationServiceDescStats stats : mAtoms.imsRegistrationServiceDescStats) { 1420 if (stats.carrierId == key.carrierId 1421 && stats.slotId == key.slotId 1422 && stats.serviceIdName == key.serviceIdName 1423 && stats.serviceIdVersion == key.serviceIdVersion 1424 && stats.registrationTech == key.registrationTech) { 1425 return stats; 1426 } 1427 } 1428 return null; 1429 } 1430 1431 /** 1432 * Returns UCE Event Stats that has the same carrier id, slot id, event result, command code and 1433 * network response as the given one, or {@code null} if it does not exist. 1434 */ find(UceEventStats key)1435 private @Nullable UceEventStats find(UceEventStats key) { 1436 for (UceEventStats stats : mAtoms.uceEventStats) { 1437 if (stats.carrierId == key.carrierId 1438 && stats.slotId == key.slotId 1439 && stats.type == key.type 1440 && stats.successful == key.successful 1441 && stats.commandCode == key.commandCode 1442 && stats.networkResponse == key.networkResponse) { 1443 return stats; 1444 } 1445 } 1446 return null; 1447 } 1448 1449 /** 1450 * Returns Presence Notify Event that has the same carrier id, slot id, reason and body in 1451 * response as the given one, or {@code null} if it does not exist. 1452 */ find(PresenceNotifyEvent key)1453 private @Nullable PresenceNotifyEvent find(PresenceNotifyEvent key) { 1454 for (PresenceNotifyEvent stats : mAtoms.presenceNotifyEvent) { 1455 if (stats.carrierId == key.carrierId 1456 && stats.slotId == key.slotId 1457 && stats.reason == key.reason 1458 && stats.contentBodyReceived == key.contentBodyReceived) { 1459 return stats; 1460 } 1461 } 1462 return null; 1463 } 1464 1465 /** 1466 * Returns GBA Event that has the same carrier id, slot id, result of operation and fail reason 1467 * as the given one, or {@code null} if it does not exist. 1468 */ find(GbaEvent key)1469 private @Nullable GbaEvent find(GbaEvent key) { 1470 for (GbaEvent stats : mAtoms.gbaEvent) { 1471 if (stats.carrierId == key.carrierId 1472 && stats.slotId == key.slotId 1473 && stats.successful == key.successful 1474 && stats.failedReason == key.failedReason) { 1475 return stats; 1476 } 1477 } 1478 return null; 1479 } 1480 1481 /** 1482 * Returns Sip Transport Feature Tag Stats that has the same carrier id, slot id, feature tag 1483 * name, deregister reason, denied reason and feature tag name or custom feature tag name as 1484 * the given one, or {@code null} if it does not exist. 1485 */ find(SipTransportFeatureTagStats key)1486 private @Nullable SipTransportFeatureTagStats find(SipTransportFeatureTagStats key) { 1487 for (SipTransportFeatureTagStats stat: mAtoms.sipTransportFeatureTagStats) { 1488 if (stat.carrierId == key.carrierId 1489 && stat.slotId == key.slotId 1490 && stat.featureTagName == key.featureTagName 1491 && stat.sipTransportDeregisteredReason == key.sipTransportDeregisteredReason 1492 && stat.sipTransportDeniedReason == key.sipTransportDeniedReason) { 1493 return stat; 1494 } 1495 } 1496 return null; 1497 } 1498 1499 /** 1500 * Inserts a new element in a random position in an array with a maximum size, replacing the 1501 * least recent item if possible. 1502 */ insertAtRandomPlace(T[] storage, T instance, int maxLength)1503 private static <T> T[] insertAtRandomPlace(T[] storage, T instance, int maxLength) { 1504 final int newLength = storage.length + 1; 1505 final boolean arrayFull = (newLength > maxLength); 1506 T[] result = Arrays.copyOf(storage, arrayFull ? maxLength : newLength); 1507 if (newLength == 1) { 1508 result[0] = instance; 1509 } else if (arrayFull) { 1510 result[findItemToEvict(storage)] = instance; 1511 } else { 1512 // insert at random place (by moving the item at the random place to the end) 1513 int insertAt = sRandom.nextInt(newLength); 1514 result[newLength - 1] = result[insertAt]; 1515 result[insertAt] = instance; 1516 } 1517 return result; 1518 } 1519 1520 /** Returns index of the item suitable for eviction when the array is full. */ findItemToEvict(T[] array)1521 private static <T> int findItemToEvict(T[] array) { 1522 if (array instanceof CellularServiceState[]) { 1523 CellularServiceState[] arr = (CellularServiceState[]) array; 1524 return IntStream.range(0, arr.length) 1525 .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j) 1526 .getAsInt(); 1527 } 1528 1529 if (array instanceof CellularDataServiceSwitch[]) { 1530 CellularDataServiceSwitch[] arr = (CellularDataServiceSwitch[]) array; 1531 return IntStream.range(0, arr.length) 1532 .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j) 1533 .getAsInt(); 1534 } 1535 1536 if (array instanceof ImsRegistrationStats[]) { 1537 ImsRegistrationStats[] arr = (ImsRegistrationStats[]) array; 1538 return IntStream.range(0, arr.length) 1539 .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j) 1540 .getAsInt(); 1541 } 1542 1543 if (array instanceof ImsRegistrationTermination[]) { 1544 ImsRegistrationTermination[] arr = (ImsRegistrationTermination[]) array; 1545 return IntStream.range(0, arr.length) 1546 .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j) 1547 .getAsInt(); 1548 } 1549 1550 return sRandom.nextInt(array.length); 1551 } 1552 1553 /** Sanitizes the loaded array of atoms to avoid null values. */ sanitizeAtoms(T[] array, Class<T> cl)1554 private <T> T[] sanitizeAtoms(T[] array, Class<T> cl) { 1555 return ArrayUtils.emptyIfNull(array, cl); 1556 } 1557 1558 /** Sanitizes the loaded array of atoms loaded to avoid null values and enforce max length. */ sanitizeAtoms(T[] array, Class<T> cl, int maxLength)1559 private <T> T[] sanitizeAtoms(T[] array, Class<T> cl, int maxLength) { 1560 array = sanitizeAtoms(array, cl); 1561 if (array.length > maxLength) { 1562 return Arrays.copyOf(array, maxLength); 1563 } 1564 return array; 1565 } 1566 1567 /** Sanitizes the timestamp of the last pull loaded from persistent storage. */ sanitizeTimestamp(long timestamp)1568 private long sanitizeTimestamp(long timestamp) { 1569 return timestamp <= 0L ? getWallTimeMillis() : timestamp; 1570 } 1571 1572 /** Returns an empty PersistAtoms with pull timestamp set to current time. */ makeNewPersistAtoms()1573 private PersistAtoms makeNewPersistAtoms() { 1574 PersistAtoms atoms = new PersistAtoms(); 1575 // allow pulling only after some time so data are sufficiently aggregated 1576 long currentTime = getWallTimeMillis(); 1577 atoms.buildFingerprint = Build.FINGERPRINT; 1578 atoms.voiceCallRatUsagePullTimestampMillis = currentTime; 1579 atoms.voiceCallSessionPullTimestampMillis = currentTime; 1580 atoms.incomingSmsPullTimestampMillis = currentTime; 1581 atoms.outgoingSmsPullTimestampMillis = currentTime; 1582 atoms.carrierIdTableVersion = TelephonyManager.UNKNOWN_CARRIER_ID_LIST_VERSION; 1583 atoms.dataCallSessionPullTimestampMillis = currentTime; 1584 atoms.cellularServiceStatePullTimestampMillis = currentTime; 1585 atoms.cellularDataServiceSwitchPullTimestampMillis = currentTime; 1586 atoms.imsRegistrationStatsPullTimestampMillis = currentTime; 1587 atoms.imsRegistrationTerminationPullTimestampMillis = currentTime; 1588 atoms.networkRequestsPullTimestampMillis = currentTime; 1589 atoms.imsRegistrationFeatureTagStatsPullTimestampMillis = currentTime; 1590 atoms.rcsClientProvisioningStatsPullTimestampMillis = currentTime; 1591 atoms.rcsAcsProvisioningStatsPullTimestampMillis = currentTime; 1592 atoms.sipDelegateStatsPullTimestampMillis = currentTime; 1593 atoms.sipTransportFeatureTagStatsPullTimestampMillis = currentTime; 1594 atoms.sipMessageResponsePullTimestampMillis = currentTime; 1595 atoms.sipTransportSessionPullTimestampMillis = currentTime; 1596 atoms.imsDedicatedBearerListenerEventPullTimestampMillis = currentTime; 1597 atoms.imsDedicatedBearerEventPullTimestampMillis = currentTime; 1598 atoms.imsRegistrationServiceDescStatsPullTimestampMillis = currentTime; 1599 atoms.uceEventStatsPullTimestampMillis = currentTime; 1600 atoms.presenceNotifyEventPullTimestampMillis = currentTime; 1601 atoms.gbaEventPullTimestampMillis = currentTime; 1602 1603 Rlog.d(TAG, "created new PersistAtoms"); 1604 return atoms; 1605 } 1606 1607 @VisibleForTesting getWallTimeMillis()1608 protected long getWallTimeMillis() { 1609 // Epoch time in UTC, preserved across reboots, but can be adjusted e.g. by the user or NTP 1610 return System.currentTimeMillis(); 1611 } 1612 } 1613