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.HOUR_IN_MILLIS; 20 import static android.text.format.DateUtils.MINUTE_IN_MILLIS; 21 import static android.text.format.DateUtils.SECOND_IN_MILLIS; 22 23 import static com.android.internal.telephony.TelephonyStatsLog.CARRIER_ID_TABLE_VERSION; 24 import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_DATA_SERVICE_SWITCH; 25 import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE; 26 import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION; 27 import static com.android.internal.telephony.TelephonyStatsLog.GBA_EVENT; 28 import static com.android.internal.telephony.TelephonyStatsLog.IMS_DEDICATED_BEARER_EVENT; 29 import static com.android.internal.telephony.TelephonyStatsLog.IMS_DEDICATED_BEARER_LISTENER_EVENT; 30 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_FEATURE_TAG_STATS; 31 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS; 32 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_STATS; 33 import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_TERMINATION; 34 import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS; 35 import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS; 36 import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT; 37 import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS; 38 import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS; 39 import static com.android.internal.telephony.TelephonyStatsLog.SIM_SLOT_STATE; 40 import static com.android.internal.telephony.TelephonyStatsLog.SIP_DELEGATE_STATS; 41 import static com.android.internal.telephony.TelephonyStatsLog.SIP_MESSAGE_RESPONSE; 42 import static com.android.internal.telephony.TelephonyStatsLog.SIP_TRANSPORT_FEATURE_TAG_STATS; 43 import static com.android.internal.telephony.TelephonyStatsLog.SIP_TRANSPORT_SESSION; 44 import static com.android.internal.telephony.TelephonyStatsLog.SUPPORTED_RADIO_ACCESS_FAMILY; 45 import static com.android.internal.telephony.TelephonyStatsLog.TELEPHONY_NETWORK_REQUESTS; 46 import static com.android.internal.telephony.TelephonyStatsLog.UCE_EVENT_STATS; 47 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE; 48 import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION; 49 50 import android.annotation.Nullable; 51 import android.app.StatsManager; 52 import android.content.Context; 53 import android.util.StatsEvent; 54 55 import com.android.internal.annotations.VisibleForTesting; 56 import com.android.internal.telephony.Phone; 57 import com.android.internal.telephony.PhoneFactory; 58 import com.android.internal.telephony.TelephonyStatsLog; 59 import com.android.internal.telephony.imsphone.ImsPhone; 60 import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch; 61 import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState; 62 import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession; 63 import com.android.internal.telephony.nano.PersistAtomsProto.GbaEvent; 64 import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerEvent; 65 import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerListenerEvent; 66 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationFeatureTagStats; 67 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationServiceDescStats; 68 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats; 69 import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination; 70 import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms; 71 import com.android.internal.telephony.nano.PersistAtomsProto.NetworkRequests; 72 import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms; 73 import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent; 74 import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats; 75 import com.android.internal.telephony.nano.PersistAtomsProto.RcsClientProvisioningStats; 76 import com.android.internal.telephony.nano.PersistAtomsProto.SipDelegateStats; 77 import com.android.internal.telephony.nano.PersistAtomsProto.SipMessageResponse; 78 import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportFeatureTagStats; 79 import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportSession; 80 import com.android.internal.telephony.nano.PersistAtomsProto.UceEventStats; 81 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage; 82 import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession; 83 import com.android.internal.util.ConcurrentUtils; 84 import com.android.telephony.Rlog; 85 86 import java.util.Arrays; 87 import java.util.Comparator; 88 import java.util.List; 89 import java.util.Random; 90 import java.util.Set; 91 import java.util.concurrent.ConcurrentHashMap; 92 93 /** 94 * Implements statsd pullers for Telephony. 95 * 96 * <p>This class registers pullers to statsd, which will be called once a day to obtain telephony 97 * statistics that cannot be sent to statsd in real time. 98 */ 99 public class MetricsCollector implements StatsManager.StatsPullAtomCallback { 100 private static final String TAG = MetricsCollector.class.getSimpleName(); 101 102 /** Disables various restrictions to ease debugging during development. */ 103 private static final boolean DBG = false; // STOPSHIP if true 104 105 /** 106 * Sets atom pull cool down to 23 hours to help enforcing privacy requirement. 107 * 108 * <p>Applies to certain atoms. The interval of 23 hours leaves some margin for pull operations 109 * that occur once a day. 110 */ 111 private static final long MIN_COOLDOWN_MILLIS = 112 DBG ? 10L * SECOND_IN_MILLIS : 23L * HOUR_IN_MILLIS; 113 114 /** 115 * Buckets with less than these many calls will be dropped. 116 * 117 * <p>Applies to metrics with duration fields. Currently used by voice call RAT usages. 118 */ 119 private static final long MIN_CALLS_PER_BUCKET = DBG ? 0L : 5L; 120 121 /** Bucket size in milliseconds to round call durations into. */ 122 private static final long DURATION_BUCKET_MILLIS = 123 DBG ? 2L * SECOND_IN_MILLIS : 5L * MINUTE_IN_MILLIS; 124 125 private static final StatsManager.PullAtomMetadata POLICY_PULL_DAILY = 126 new StatsManager.PullAtomMetadata.Builder() 127 .setCoolDownMillis(MIN_COOLDOWN_MILLIS) 128 .build(); 129 130 private PersistAtomsStorage mStorage; 131 private final StatsManager mStatsManager; 132 private final AirplaneModeStats mAirplaneModeStats; 133 private final Set<DataCallSessionStats> mOngoingDataCallStats = ConcurrentHashMap.newKeySet(); 134 private static final Random sRandom = new Random(); 135 MetricsCollector(Context context)136 public MetricsCollector(Context context) { 137 mStorage = new PersistAtomsStorage(context); 138 mStatsManager = (StatsManager) context.getSystemService(Context.STATS_MANAGER); 139 if (mStatsManager != null) { 140 registerAtom(CELLULAR_DATA_SERVICE_SWITCH, POLICY_PULL_DAILY); 141 registerAtom(CELLULAR_SERVICE_STATE, POLICY_PULL_DAILY); 142 registerAtom(SIM_SLOT_STATE, null); 143 registerAtom(SUPPORTED_RADIO_ACCESS_FAMILY, null); 144 registerAtom(VOICE_CALL_RAT_USAGE, POLICY_PULL_DAILY); 145 registerAtom(VOICE_CALL_SESSION, POLICY_PULL_DAILY); 146 registerAtom(INCOMING_SMS, POLICY_PULL_DAILY); 147 registerAtom(OUTGOING_SMS, POLICY_PULL_DAILY); 148 registerAtom(CARRIER_ID_TABLE_VERSION, null); 149 registerAtom(DATA_CALL_SESSION, POLICY_PULL_DAILY); 150 registerAtom(IMS_REGISTRATION_STATS, POLICY_PULL_DAILY); 151 registerAtom(IMS_REGISTRATION_TERMINATION, POLICY_PULL_DAILY); 152 registerAtom(TELEPHONY_NETWORK_REQUESTS, POLICY_PULL_DAILY); 153 registerAtom(IMS_REGISTRATION_FEATURE_TAG_STATS, POLICY_PULL_DAILY); 154 registerAtom(RCS_CLIENT_PROVISIONING_STATS, POLICY_PULL_DAILY); 155 registerAtom(RCS_ACS_PROVISIONING_STATS, POLICY_PULL_DAILY); 156 registerAtom(SIP_DELEGATE_STATS, POLICY_PULL_DAILY); 157 registerAtom(SIP_TRANSPORT_FEATURE_TAG_STATS, POLICY_PULL_DAILY); 158 registerAtom(SIP_MESSAGE_RESPONSE, POLICY_PULL_DAILY); 159 registerAtom(SIP_TRANSPORT_SESSION, POLICY_PULL_DAILY); 160 registerAtom(IMS_DEDICATED_BEARER_LISTENER_EVENT, POLICY_PULL_DAILY); 161 registerAtom(IMS_DEDICATED_BEARER_EVENT, POLICY_PULL_DAILY); 162 registerAtom(IMS_REGISTRATION_SERVICE_DESC_STATS, POLICY_PULL_DAILY); 163 registerAtom(UCE_EVENT_STATS, POLICY_PULL_DAILY); 164 registerAtom(PRESENCE_NOTIFY_EVENT, POLICY_PULL_DAILY); 165 registerAtom(GBA_EVENT, POLICY_PULL_DAILY); 166 167 Rlog.d(TAG, "registered"); 168 } else { 169 Rlog.e(TAG, "could not get StatsManager, atoms not registered"); 170 } 171 172 mAirplaneModeStats = new AirplaneModeStats(context); 173 } 174 175 /** Replaces the {@link PersistAtomsStorage} backing the puller. Used during unit tests. */ 176 @VisibleForTesting setPersistAtomsStorage(PersistAtomsStorage storage)177 public void setPersistAtomsStorage(PersistAtomsStorage storage) { 178 mStorage = storage; 179 } 180 181 /** 182 * {@inheritDoc} 183 * 184 * @return {@link StatsManager#PULL_SUCCESS} with list of atoms (potentially empty) if pull 185 * succeeded, {@link StatsManager#PULL_SKIP} if pull was too frequent or atom ID is 186 * unexpected. 187 */ 188 @Override onPullAtom(int atomTag, List<StatsEvent> data)189 public int onPullAtom(int atomTag, List<StatsEvent> data) { 190 switch (atomTag) { 191 case CELLULAR_DATA_SERVICE_SWITCH: 192 return pullCellularDataServiceSwitch(data); 193 case CELLULAR_SERVICE_STATE: 194 return pullCellularServiceState(data); 195 case SIM_SLOT_STATE: 196 return pullSimSlotState(data); 197 case SUPPORTED_RADIO_ACCESS_FAMILY: 198 return pullSupportedRadioAccessFamily(data); 199 case VOICE_CALL_RAT_USAGE: 200 return pullVoiceCallRatUsages(data); 201 case VOICE_CALL_SESSION: 202 return pullVoiceCallSessions(data); 203 case INCOMING_SMS: 204 return pullIncomingSms(data); 205 case OUTGOING_SMS: 206 return pullOutgoingSms(data); 207 case CARRIER_ID_TABLE_VERSION: 208 return pullCarrierIdTableVersion(data); 209 case DATA_CALL_SESSION: 210 return pullDataCallSession(data); 211 case IMS_REGISTRATION_STATS: 212 return pullImsRegistrationStats(data); 213 case IMS_REGISTRATION_TERMINATION: 214 return pullImsRegistrationTermination(data); 215 case TELEPHONY_NETWORK_REQUESTS: 216 return pullTelephonyNetworkRequests(data); 217 case IMS_REGISTRATION_FEATURE_TAG_STATS: 218 return pullImsRegistrationFeatureTagStats(data); 219 case RCS_CLIENT_PROVISIONING_STATS: 220 return pullRcsClientProvisioningStats(data); 221 case RCS_ACS_PROVISIONING_STATS: 222 return pullRcsAcsProvisioningStats(data); 223 case SIP_DELEGATE_STATS: 224 return pullSipDelegateStats(data); 225 case SIP_TRANSPORT_FEATURE_TAG_STATS: 226 return pullSipTransportFeatureTagStats(data); 227 case SIP_MESSAGE_RESPONSE: 228 return pullSipMessageResponse(data); 229 case SIP_TRANSPORT_SESSION: 230 return pullSipTransportSession(data); 231 case IMS_DEDICATED_BEARER_LISTENER_EVENT: 232 return pullImsDedicatedBearerListenerEvent(data); 233 case IMS_DEDICATED_BEARER_EVENT: 234 return pullImsDedicatedBearerEvent(data); 235 case IMS_REGISTRATION_SERVICE_DESC_STATS: 236 return pullImsRegistrationServiceDescStats(data); 237 case UCE_EVENT_STATS: 238 return pullUceEventStats(data); 239 case PRESENCE_NOTIFY_EVENT: 240 return pullPresenceNotifyEvent(data); 241 case GBA_EVENT: 242 return pullGbaEvent(data); 243 default: 244 Rlog.e(TAG, String.format("unexpected atom ID %d", atomTag)); 245 return StatsManager.PULL_SKIP; 246 } 247 } 248 249 /** Returns the {@link PersistAtomsStorage} backing the puller. */ getAtomsStorage()250 public PersistAtomsStorage getAtomsStorage() { 251 return mStorage; 252 } 253 254 /** 255 * Registers a {@link DataCallSessionStats} which will be pinged for on-going data calls when 256 * data call atoms are pulled. 257 */ registerOngoingDataCallStat(DataCallSessionStats call)258 public void registerOngoingDataCallStat(DataCallSessionStats call) { 259 mOngoingDataCallStats.add(call); 260 } 261 262 /** Unregisters a {@link DataCallSessionStats} when it no longer handles an active data call. */ unregisterOngoingDataCallStat(DataCallSessionStats call)263 public void unregisterOngoingDataCallStat(DataCallSessionStats call) { 264 mOngoingDataCallStats.remove(call); 265 } 266 pullSimSlotState(List<StatsEvent> data)267 private static int pullSimSlotState(List<StatsEvent> data) { 268 SimSlotState state; 269 try { 270 state = SimSlotState.getCurrentState(); 271 } catch (RuntimeException e) { 272 // UiccController has not been made yet 273 return StatsManager.PULL_SKIP; 274 } 275 276 data.add( 277 TelephonyStatsLog.buildStatsEvent( 278 SIM_SLOT_STATE, 279 state.numActiveSlots, 280 state.numActiveSims, 281 state.numActiveEsims)); 282 return StatsManager.PULL_SUCCESS; 283 } 284 pullSupportedRadioAccessFamily(List<StatsEvent> data)285 private static int pullSupportedRadioAccessFamily(List<StatsEvent> data) { 286 Phone[] phones = getPhonesIfAny(); 287 if (phones.length == 0) { 288 return StatsManager.PULL_SKIP; 289 } 290 291 // The bitmask is defined in android.telephony.TelephonyManager.NetworkTypeBitMask 292 long rafSupported = 0L; 293 for (Phone phone : PhoneFactory.getPhones()) { 294 rafSupported |= phone.getRadioAccessFamily(); 295 } 296 297 data.add(TelephonyStatsLog.buildStatsEvent(SUPPORTED_RADIO_ACCESS_FAMILY, rafSupported)); 298 return StatsManager.PULL_SUCCESS; 299 } 300 pullCarrierIdTableVersion(List<StatsEvent> data)301 private static int pullCarrierIdTableVersion(List<StatsEvent> data) { 302 Phone[] phones = getPhonesIfAny(); 303 if (phones.length == 0) { 304 return StatsManager.PULL_SKIP; 305 } else { 306 // All phones should have the same version of the carrier ID table, so only query the 307 // first one. 308 int version = phones[0].getCarrierIdListVersion(); 309 data.add(TelephonyStatsLog.buildStatsEvent(CARRIER_ID_TABLE_VERSION, version)); 310 return StatsManager.PULL_SUCCESS; 311 } 312 } 313 pullVoiceCallRatUsages(List<StatsEvent> data)314 private int pullVoiceCallRatUsages(List<StatsEvent> data) { 315 VoiceCallRatUsage[] usages = mStorage.getVoiceCallRatUsages(MIN_COOLDOWN_MILLIS); 316 if (usages != null) { 317 // sort by carrier/RAT and remove buckets with insufficient number of calls 318 Arrays.stream(usages) 319 .sorted( 320 Comparator.comparingLong( 321 usage -> ((long) usage.carrierId << 32) | usage.rat)) 322 .filter(usage -> usage.callCount >= MIN_CALLS_PER_BUCKET) 323 .forEach(usage -> data.add(buildStatsEvent(usage))); 324 Rlog.d( 325 TAG, 326 String.format( 327 "%d out of %d VOICE_CALL_RAT_USAGE pulled", 328 data.size(), usages.length)); 329 return StatsManager.PULL_SUCCESS; 330 } else { 331 Rlog.w(TAG, "VOICE_CALL_RAT_USAGE pull too frequent, skipping"); 332 return StatsManager.PULL_SKIP; 333 } 334 } 335 pullVoiceCallSessions(List<StatsEvent> data)336 private int pullVoiceCallSessions(List<StatsEvent> data) { 337 VoiceCallSession[] calls = mStorage.getVoiceCallSessions(MIN_COOLDOWN_MILLIS); 338 if (calls != null) { 339 // call session list is already shuffled when calls were inserted 340 Arrays.stream(calls).forEach(call -> data.add(buildStatsEvent(call))); 341 return StatsManager.PULL_SUCCESS; 342 } else { 343 Rlog.w(TAG, "VOICE_CALL_SESSION pull too frequent, skipping"); 344 return StatsManager.PULL_SKIP; 345 } 346 } 347 pullIncomingSms(List<StatsEvent> data)348 private int pullIncomingSms(List<StatsEvent> data) { 349 IncomingSms[] smsList = mStorage.getIncomingSms(MIN_COOLDOWN_MILLIS); 350 if (smsList != null) { 351 // SMS list is already shuffled when SMS were inserted 352 Arrays.stream(smsList).forEach(sms -> data.add(buildStatsEvent(sms))); 353 return StatsManager.PULL_SUCCESS; 354 } else { 355 Rlog.w(TAG, "INCOMING_SMS pull too frequent, skipping"); 356 return StatsManager.PULL_SKIP; 357 } 358 } 359 pullOutgoingSms(List<StatsEvent> data)360 private int pullOutgoingSms(List<StatsEvent> data) { 361 OutgoingSms[] smsList = mStorage.getOutgoingSms(MIN_COOLDOWN_MILLIS); 362 if (smsList != null) { 363 // SMS list is already shuffled when SMS were inserted 364 Arrays.stream(smsList).forEach(sms -> data.add(buildStatsEvent(sms))); 365 return StatsManager.PULL_SUCCESS; 366 } else { 367 Rlog.w(TAG, "OUTGOING_SMS pull too frequent, skipping"); 368 return StatsManager.PULL_SKIP; 369 } 370 } 371 pullDataCallSession(List<StatsEvent> data)372 private int pullDataCallSession(List<StatsEvent> data) { 373 // Include ongoing data call segments 374 for (DataCallSessionStats stats : mOngoingDataCallStats) { 375 stats.conclude(); 376 } 377 378 DataCallSession[] dataCallSessions = mStorage.getDataCallSessions(MIN_COOLDOWN_MILLIS); 379 if (dataCallSessions != null) { 380 Arrays.stream(dataCallSessions) 381 .forEach(dataCall -> data.add(buildStatsEvent(dataCall))); 382 return StatsManager.PULL_SUCCESS; 383 } else { 384 Rlog.w(TAG, "DATA_CALL_SESSION pull too frequent, skipping"); 385 return StatsManager.PULL_SKIP; 386 } 387 } 388 pullCellularDataServiceSwitch(List<StatsEvent> data)389 private int pullCellularDataServiceSwitch(List<StatsEvent> data) { 390 CellularDataServiceSwitch[] persistAtoms = 391 mStorage.getCellularDataServiceSwitches(MIN_COOLDOWN_MILLIS); 392 if (persistAtoms != null) { 393 // list is already shuffled when instances were inserted 394 Arrays.stream(persistAtoms) 395 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 396 return StatsManager.PULL_SUCCESS; 397 } else { 398 Rlog.w(TAG, "CELLULAR_DATA_SERVICE_SWITCH pull too frequent, skipping"); 399 return StatsManager.PULL_SKIP; 400 } 401 } 402 pullCellularServiceState(List<StatsEvent> data)403 private int pullCellularServiceState(List<StatsEvent> data) { 404 // Include the latest durations 405 for (Phone phone : getPhonesIfAny()) { 406 phone.getServiceStateTracker().getServiceStateStats().conclude(); 407 } 408 409 CellularServiceState[] persistAtoms = 410 mStorage.getCellularServiceStates(MIN_COOLDOWN_MILLIS); 411 if (persistAtoms != null) { 412 // list is already shuffled when instances were inserted 413 Arrays.stream(persistAtoms) 414 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 415 return StatsManager.PULL_SUCCESS; 416 } else { 417 Rlog.w(TAG, "CELLULAR_SERVICE_STATE pull too frequent, skipping"); 418 return StatsManager.PULL_SKIP; 419 } 420 } 421 pullImsRegistrationStats(List<StatsEvent> data)422 private int pullImsRegistrationStats(List<StatsEvent> data) { 423 // Include the latest durations 424 for (Phone phone : getPhonesIfAny()) { 425 ImsPhone imsPhone = (ImsPhone) phone.getImsPhone(); 426 if (imsPhone != null) { 427 imsPhone.getImsStats().conclude(); 428 } 429 } 430 431 ImsRegistrationStats[] persistAtoms = mStorage.getImsRegistrationStats(MIN_COOLDOWN_MILLIS); 432 if (persistAtoms != null) { 433 // list is already shuffled when instances were inserted 434 Arrays.stream(persistAtoms) 435 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 436 return StatsManager.PULL_SUCCESS; 437 } else { 438 Rlog.w(TAG, "IMS_REGISTRATION_STATS pull too frequent, skipping"); 439 return StatsManager.PULL_SKIP; 440 } 441 } 442 pullImsRegistrationTermination(List<StatsEvent> data)443 private int pullImsRegistrationTermination(List<StatsEvent> data) { 444 ImsRegistrationTermination[] persistAtoms = 445 mStorage.getImsRegistrationTerminations(MIN_COOLDOWN_MILLIS); 446 if (persistAtoms != null) { 447 // list is already shuffled when instances were inserted 448 Arrays.stream(persistAtoms) 449 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 450 return StatsManager.PULL_SUCCESS; 451 } else { 452 Rlog.w(TAG, "IMS_REGISTRATION_TERMINATION pull too frequent, skipping"); 453 return StatsManager.PULL_SKIP; 454 } 455 } 456 pullTelephonyNetworkRequests(List<StatsEvent> data)457 private int pullTelephonyNetworkRequests(List<StatsEvent> data) { 458 NetworkRequests[] persistAtoms = mStorage.getNetworkRequests(MIN_COOLDOWN_MILLIS); 459 if (persistAtoms != null) { 460 Arrays.stream(persistAtoms) 461 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 462 return StatsManager.PULL_SUCCESS; 463 } else { 464 Rlog.w(TAG, "TELEPHONY_NETWORK_REQUESTS pull too frequent, skipping"); 465 return StatsManager.PULL_SKIP; 466 } 467 } 468 pullImsRegistrationFeatureTagStats(List<StatsEvent> data)469 private int pullImsRegistrationFeatureTagStats(List<StatsEvent> data) { 470 RcsStats.getInstance().onFlushIncompleteImsRegistrationFeatureTagStats(); 471 472 ImsRegistrationFeatureTagStats[] persistAtoms = 473 mStorage.getImsRegistrationFeatureTagStats(MIN_COOLDOWN_MILLIS); 474 if (persistAtoms != null) { 475 Arrays.stream(persistAtoms) 476 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 477 return StatsManager.PULL_SUCCESS; 478 } else { 479 Rlog.w(TAG, "IMS_REGISTRATION_FEATURE_TAG_STATS pull too frequent, skipping"); 480 return StatsManager.PULL_SKIP; 481 } 482 } 483 pullRcsClientProvisioningStats(List<StatsEvent> data)484 private int pullRcsClientProvisioningStats(List<StatsEvent> data) { 485 RcsClientProvisioningStats[] persistAtoms = 486 mStorage.getRcsClientProvisioningStats(MIN_COOLDOWN_MILLIS); 487 if (persistAtoms != null) { 488 Arrays.stream(persistAtoms) 489 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 490 return StatsManager.PULL_SUCCESS; 491 } else { 492 Rlog.w(TAG, "RCS_CLIENT_PROVISIONING_STATS pull too frequent, skipping"); 493 return StatsManager.PULL_SKIP; 494 } 495 } 496 pullRcsAcsProvisioningStats(List<StatsEvent> data)497 private int pullRcsAcsProvisioningStats(List<StatsEvent> data) { 498 RcsStats.getInstance().onFlushIncompleteRcsAcsProvisioningStats(); 499 500 RcsAcsProvisioningStats[] persistAtoms = 501 mStorage.getRcsAcsProvisioningStats(MIN_COOLDOWN_MILLIS); 502 if (persistAtoms != null) { 503 Arrays.stream(persistAtoms) 504 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 505 return StatsManager.PULL_SUCCESS; 506 } else { 507 Rlog.w(TAG, "RCS_ACS_PROVISIONING_STATS pull too frequent, skipping"); 508 return StatsManager.PULL_SKIP; 509 } 510 } 511 pullSipDelegateStats(List<StatsEvent> data)512 private int pullSipDelegateStats(List<StatsEvent> data) { 513 SipDelegateStats[] persisAtoms = 514 mStorage.getSipDelegateStats(MIN_COOLDOWN_MILLIS); 515 if (persisAtoms != null) { 516 Arrays.stream(persisAtoms) 517 .forEach(persisAtom -> data.add(buildStatsEvent(persisAtom))); 518 return StatsManager.PULL_SUCCESS; 519 } else { 520 Rlog.w(TAG, "SIP_DELEGATE_STATS pull too frequent, skipping"); 521 return StatsManager.PULL_SKIP; 522 } 523 } 524 pullSipTransportFeatureTagStats(List<StatsEvent> data)525 private int pullSipTransportFeatureTagStats(List<StatsEvent> data) { 526 RcsStats.getInstance().concludeSipTransportFeatureTagsStat(); 527 528 SipTransportFeatureTagStats[] persisAtoms = 529 mStorage.getSipTransportFeatureTagStats(MIN_COOLDOWN_MILLIS); 530 if (persisAtoms != null) { 531 Arrays.stream(persisAtoms) 532 .forEach(persisAtom -> data.add(buildStatsEvent(persisAtom))); 533 return StatsManager.PULL_SUCCESS; 534 } else { 535 Rlog.w(TAG, "SIP_DELEGATE_STATS pull too frequent, skipping"); 536 return StatsManager.PULL_SKIP; 537 } 538 } 539 pullSipMessageResponse(List<StatsEvent> data)540 private int pullSipMessageResponse(List<StatsEvent> data) { 541 SipMessageResponse[] persistAtoms = 542 mStorage.getSipMessageResponse(MIN_COOLDOWN_MILLIS); 543 if (persistAtoms != null) { 544 Arrays.stream(persistAtoms) 545 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 546 return StatsManager.PULL_SUCCESS; 547 } else { 548 Rlog.w(TAG, "RCS_SIP_MESSAGE_RESPONSE pull too frequent, skipping"); 549 return StatsManager.PULL_SKIP; 550 } 551 } 552 pullSipTransportSession(List<StatsEvent> data)553 private int pullSipTransportSession(List<StatsEvent> data) { 554 SipTransportSession[] persistAtoms = 555 mStorage.getSipTransportSession(MIN_COOLDOWN_MILLIS); 556 if (persistAtoms != null) { 557 Arrays.stream(persistAtoms) 558 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 559 return StatsManager.PULL_SUCCESS; 560 } else { 561 Rlog.w(TAG, "RCS_SIP_TRANSPORT_SESSION pull too frequent, skipping"); 562 return StatsManager.PULL_SKIP; 563 } 564 } 565 pullImsDedicatedBearerListenerEvent(List<StatsEvent> data)566 private int pullImsDedicatedBearerListenerEvent(List<StatsEvent> data) { 567 ImsDedicatedBearerListenerEvent[] persistAtoms = 568 mStorage.getImsDedicatedBearerListenerEvent(MIN_COOLDOWN_MILLIS); 569 if (persistAtoms != null) { 570 Arrays.stream(persistAtoms) 571 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 572 return StatsManager.PULL_SUCCESS; 573 } else { 574 Rlog.w(TAG, "IMS_DEDICATED_BEARER_LISTENER_EVENT pull too frequent, skipping"); 575 return StatsManager.PULL_SKIP; 576 } 577 } 578 pullImsDedicatedBearerEvent(List<StatsEvent> data)579 private int pullImsDedicatedBearerEvent(List<StatsEvent> data) { 580 ImsDedicatedBearerEvent[] persistAtoms = 581 mStorage.getImsDedicatedBearerEvent(MIN_COOLDOWN_MILLIS); 582 if (persistAtoms != null) { 583 Arrays.stream(persistAtoms) 584 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 585 return StatsManager.PULL_SUCCESS; 586 } else { 587 Rlog.w(TAG, "IMS_DEDICATED_BEARER_EVENT pull too frequent, skipping"); 588 return StatsManager.PULL_SKIP; 589 } 590 } 591 pullImsRegistrationServiceDescStats(List<StatsEvent> data)592 private int pullImsRegistrationServiceDescStats(List<StatsEvent> data) { 593 RcsStats.getInstance().onFlushIncompleteImsRegistrationServiceDescStats(); 594 ImsRegistrationServiceDescStats[] persistAtoms = 595 mStorage.getImsRegistrationServiceDescStats(MIN_COOLDOWN_MILLIS); 596 if (persistAtoms != null) { 597 Arrays.stream(persistAtoms) 598 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 599 return StatsManager.PULL_SUCCESS; 600 } else { 601 Rlog.w(TAG, "IMS_REGISTRATION_SERVICE_DESC_STATS pull too frequent, skipping"); 602 return StatsManager.PULL_SKIP; 603 } 604 } 605 pullUceEventStats(List<StatsEvent> data)606 private int pullUceEventStats(List<StatsEvent> data) { 607 UceEventStats[] persistAtoms = mStorage.getUceEventStats(MIN_COOLDOWN_MILLIS); 608 if (persistAtoms != null) { 609 Arrays.stream(persistAtoms) 610 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 611 return StatsManager.PULL_SUCCESS; 612 } else { 613 Rlog.w(TAG, "UCE_EVENT_STATS pull too frequent, skipping"); 614 return StatsManager.PULL_SKIP; 615 } 616 } 617 pullPresenceNotifyEvent(List<StatsEvent> data)618 private int pullPresenceNotifyEvent(List<StatsEvent> data) { 619 PresenceNotifyEvent[] persistAtoms = mStorage.getPresenceNotifyEvent(MIN_COOLDOWN_MILLIS); 620 if (persistAtoms != null) { 621 Arrays.stream(persistAtoms) 622 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 623 return StatsManager.PULL_SUCCESS; 624 } else { 625 Rlog.w(TAG, "PRESENCE_NOTIFY_EVENT pull too frequent, skipping"); 626 return StatsManager.PULL_SKIP; 627 } 628 } 629 pullGbaEvent(List<StatsEvent> data)630 private int pullGbaEvent(List<StatsEvent> data) { 631 GbaEvent[] persistAtoms = mStorage.getGbaEvent(MIN_COOLDOWN_MILLIS); 632 if (persistAtoms != null) { 633 Arrays.stream(persistAtoms) 634 .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom))); 635 return StatsManager.PULL_SUCCESS; 636 } else { 637 Rlog.w(TAG, "GBA_EVENT pull too frequent, skipping"); 638 return StatsManager.PULL_SKIP; 639 } 640 } 641 642 /** Registers a pulled atom ID {@code atomId} with optional {@code policy} for pulling. */ registerAtom(int atomId, @Nullable StatsManager.PullAtomMetadata policy)643 private void registerAtom(int atomId, @Nullable StatsManager.PullAtomMetadata policy) { 644 mStatsManager.setPullAtomCallback(atomId, policy, ConcurrentUtils.DIRECT_EXECUTOR, this); 645 } 646 buildStatsEvent(CellularDataServiceSwitch serviceSwitch)647 private static StatsEvent buildStatsEvent(CellularDataServiceSwitch serviceSwitch) { 648 return TelephonyStatsLog.buildStatsEvent( 649 CELLULAR_DATA_SERVICE_SWITCH, 650 serviceSwitch.ratFrom, 651 serviceSwitch.ratTo, 652 serviceSwitch.simSlotIndex, 653 serviceSwitch.isMultiSim, 654 serviceSwitch.carrierId, 655 serviceSwitch.switchCount); 656 } 657 buildStatsEvent(CellularServiceState state)658 private static StatsEvent buildStatsEvent(CellularServiceState state) { 659 return TelephonyStatsLog.buildStatsEvent( 660 CELLULAR_SERVICE_STATE, 661 state.voiceRat, 662 state.dataRat, 663 state.voiceRoamingType, 664 state.dataRoamingType, 665 state.isEndc, 666 state.simSlotIndex, 667 state.isMultiSim, 668 state.carrierId, 669 (int) (round(state.totalTimeMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS)); 670 } 671 buildStatsEvent(VoiceCallRatUsage usage)672 private static StatsEvent buildStatsEvent(VoiceCallRatUsage usage) { 673 return TelephonyStatsLog.buildStatsEvent( 674 VOICE_CALL_RAT_USAGE, 675 usage.carrierId, 676 usage.rat, 677 round(usage.totalDurationMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS, 678 usage.callCount); 679 } 680 buildStatsEvent(VoiceCallSession session)681 private static StatsEvent buildStatsEvent(VoiceCallSession session) { 682 return TelephonyStatsLog.buildStatsEvent( 683 VOICE_CALL_SESSION, 684 session.bearerAtStart, 685 session.bearerAtEnd, 686 session.direction, 687 session.setupDuration, 688 session.setupFailed, 689 session.disconnectReasonCode, 690 session.disconnectExtraCode, 691 session.disconnectExtraMessage, 692 session.ratAtStart, 693 session.ratAtEnd, 694 session.ratSwitchCount, 695 session.codecBitmask, 696 session.concurrentCallCountAtStart, 697 session.concurrentCallCountAtEnd, 698 session.simSlotIndex, 699 session.isMultiSim, 700 session.isEsim, 701 session.carrierId, 702 session.srvccCompleted, 703 session.srvccFailureCount, 704 session.srvccCancellationCount, 705 session.rttEnabled, 706 session.isEmergency, 707 session.isRoaming, 708 // workaround: dimension required for keeping multiple pulled atoms 709 sRandom.nextInt(), 710 // New fields introduced in Android S 711 session.signalStrengthAtEnd, 712 session.bandAtEnd, 713 session.setupDurationMillis, 714 session.mainCodecQuality, 715 session.videoEnabled, 716 session.ratAtConnected, 717 session.isMultiparty); 718 } 719 buildStatsEvent(IncomingSms sms)720 private static StatsEvent buildStatsEvent(IncomingSms sms) { 721 return TelephonyStatsLog.buildStatsEvent( 722 INCOMING_SMS, 723 sms.smsFormat, 724 sms.smsTech, 725 sms.rat, 726 sms.smsType, 727 sms.totalParts, 728 sms.receivedParts, 729 sms.blocked, 730 sms.error, 731 sms.isRoaming, 732 sms.simSlotIndex, 733 sms.isMultiSim, 734 sms.isEsim, 735 sms.carrierId, 736 sms.messageId); 737 } 738 buildStatsEvent(OutgoingSms sms)739 private static StatsEvent buildStatsEvent(OutgoingSms sms) { 740 return TelephonyStatsLog.buildStatsEvent( 741 OUTGOING_SMS, 742 sms.smsFormat, 743 sms.smsTech, 744 sms.rat, 745 sms.sendResult, 746 sms.errorCode, 747 sms.isRoaming, 748 sms.isFromDefaultApp, 749 sms.simSlotIndex, 750 sms.isMultiSim, 751 sms.isEsim, 752 sms.carrierId, 753 sms.messageId, 754 sms.retryId); 755 } 756 buildStatsEvent(DataCallSession dataCallSession)757 private static StatsEvent buildStatsEvent(DataCallSession dataCallSession) { 758 return TelephonyStatsLog.buildStatsEvent( 759 DATA_CALL_SESSION, 760 dataCallSession.dimension, 761 dataCallSession.isMultiSim, 762 dataCallSession.isEsim, 763 0, // profile is deprecated, so we default to 0 764 dataCallSession.apnTypeBitmask, 765 dataCallSession.carrierId, 766 dataCallSession.isRoaming, 767 dataCallSession.ratAtEnd, 768 dataCallSession.oosAtEnd, 769 dataCallSession.ratSwitchCount, 770 dataCallSession.isOpportunistic, 771 dataCallSession.ipType, 772 dataCallSession.setupFailed, 773 dataCallSession.failureCause, 774 dataCallSession.suggestedRetryMillis, 775 dataCallSession.deactivateReason, 776 round(dataCallSession.durationMinutes, DURATION_BUCKET_MILLIS / MINUTE_IN_MILLIS), 777 dataCallSession.ongoing, 778 dataCallSession.bandAtEnd); 779 } 780 buildStatsEvent(ImsRegistrationStats stats)781 private static StatsEvent buildStatsEvent(ImsRegistrationStats stats) { 782 return TelephonyStatsLog.buildStatsEvent( 783 IMS_REGISTRATION_STATS, 784 stats.carrierId, 785 stats.simSlotIndex, 786 stats.rat, 787 (int) (round(stats.registeredMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS), 788 (int) (round(stats.voiceCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS), 789 (int) 790 (round(stats.voiceAvailableMillis, DURATION_BUCKET_MILLIS) 791 / SECOND_IN_MILLIS), 792 (int) (round(stats.smsCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS), 793 (int) (round(stats.smsAvailableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS), 794 (int) (round(stats.videoCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS), 795 (int) 796 (round(stats.videoAvailableMillis, DURATION_BUCKET_MILLIS) 797 / SECOND_IN_MILLIS), 798 (int) (round(stats.utCapableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS), 799 (int) (round(stats.utAvailableMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS)); 800 } 801 buildStatsEvent(ImsRegistrationTermination termination)802 private static StatsEvent buildStatsEvent(ImsRegistrationTermination termination) { 803 return TelephonyStatsLog.buildStatsEvent( 804 IMS_REGISTRATION_TERMINATION, 805 termination.carrierId, 806 termination.isMultiSim, 807 termination.ratAtEnd, 808 termination.setupFailed, 809 termination.reasonCode, 810 termination.extraCode, 811 termination.extraMessage, 812 termination.count); 813 } 814 buildStatsEvent(NetworkRequests networkRequests)815 private static StatsEvent buildStatsEvent(NetworkRequests networkRequests) { 816 return TelephonyStatsLog.buildStatsEvent( 817 TELEPHONY_NETWORK_REQUESTS, 818 networkRequests.carrierId, 819 networkRequests.enterpriseRequestCount, 820 networkRequests.enterpriseReleaseCount); 821 } 822 buildStatsEvent(ImsRegistrationFeatureTagStats stats)823 private static StatsEvent buildStatsEvent(ImsRegistrationFeatureTagStats stats) { 824 return TelephonyStatsLog.buildStatsEvent( 825 IMS_REGISTRATION_FEATURE_TAG_STATS, 826 stats.carrierId, 827 stats.slotId, 828 stats.featureTagName, 829 stats.registrationTech, 830 (int) (round(stats.registeredMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS)); 831 } 832 buildStatsEvent(RcsClientProvisioningStats stats)833 private static StatsEvent buildStatsEvent(RcsClientProvisioningStats stats) { 834 return TelephonyStatsLog.buildStatsEvent( 835 RCS_CLIENT_PROVISIONING_STATS, 836 stats.carrierId, 837 stats.slotId, 838 stats.event, 839 stats.count); 840 } 841 buildStatsEvent(RcsAcsProvisioningStats stats)842 private static StatsEvent buildStatsEvent(RcsAcsProvisioningStats stats) { 843 return TelephonyStatsLog.buildStatsEvent( 844 RCS_ACS_PROVISIONING_STATS, 845 stats.carrierId, 846 stats.slotId, 847 stats.responseCode, 848 stats.responseType, 849 stats.isSingleRegistrationEnabled, 850 stats.count, 851 (int) (round(stats.stateTimerMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS)); 852 } 853 buildStatsEvent(SipDelegateStats stats)854 private static StatsEvent buildStatsEvent(SipDelegateStats stats) { 855 return TelephonyStatsLog.buildStatsEvent( 856 SIP_DELEGATE_STATS, 857 stats.dimension, 858 stats.carrierId, 859 stats.slotId, 860 (int) (round(stats.uptimeMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS), 861 stats.destroyReason); 862 } 863 buildStatsEvent(SipTransportFeatureTagStats stats)864 private static StatsEvent buildStatsEvent(SipTransportFeatureTagStats stats) { 865 return TelephonyStatsLog.buildStatsEvent( 866 SIP_TRANSPORT_FEATURE_TAG_STATS, 867 stats.carrierId, 868 stats.slotId, 869 stats.featureTagName, 870 stats.sipTransportDeniedReason, 871 stats.sipTransportDeregisteredReason, 872 (int) (round(stats.associatedMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS)); 873 } 874 buildStatsEvent(SipMessageResponse stats)875 private static StatsEvent buildStatsEvent(SipMessageResponse stats) { 876 return TelephonyStatsLog.buildStatsEvent( 877 SIP_MESSAGE_RESPONSE, 878 stats.carrierId, 879 stats.slotId, 880 stats.sipMessageMethod, 881 stats.sipMessageResponse, 882 stats.sipMessageDirection, 883 stats.messageError, 884 stats.count); 885 } 886 buildStatsEvent(SipTransportSession stats)887 private static StatsEvent buildStatsEvent(SipTransportSession stats) { 888 return TelephonyStatsLog.buildStatsEvent( 889 SIP_TRANSPORT_SESSION, 890 stats.carrierId, 891 stats.slotId, 892 stats.sessionMethod, 893 stats.sipMessageDirection, 894 stats.sipResponse, 895 stats.sessionCount, 896 stats.endedGracefullyCount); 897 } 898 buildStatsEvent(ImsDedicatedBearerListenerEvent stats)899 private static StatsEvent buildStatsEvent(ImsDedicatedBearerListenerEvent stats) { 900 return TelephonyStatsLog.buildStatsEvent( 901 IMS_DEDICATED_BEARER_LISTENER_EVENT, 902 stats.carrierId, 903 stats.slotId, 904 stats.ratAtEnd, 905 stats.qci, 906 stats.dedicatedBearerEstablished, 907 stats.eventCount); 908 } 909 buildStatsEvent(ImsDedicatedBearerEvent stats)910 private static StatsEvent buildStatsEvent(ImsDedicatedBearerEvent stats) { 911 return TelephonyStatsLog.buildStatsEvent( 912 IMS_DEDICATED_BEARER_EVENT, 913 stats.carrierId, 914 stats.slotId, 915 stats.ratAtEnd, 916 stats.qci, 917 stats.bearerState, 918 stats.localConnectionInfoReceived, 919 stats.remoteConnectionInfoReceived, 920 stats.hasListeners, 921 stats.count); 922 } 923 buildStatsEvent(ImsRegistrationServiceDescStats stats)924 private static StatsEvent buildStatsEvent(ImsRegistrationServiceDescStats stats) { 925 return TelephonyStatsLog.buildStatsEvent( 926 IMS_REGISTRATION_SERVICE_DESC_STATS, 927 stats.carrierId, 928 stats.slotId, 929 stats.serviceIdName, 930 stats.serviceIdVersion, 931 stats.registrationTech, 932 (int) (round(stats.publishedMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS)); 933 } 934 buildStatsEvent(UceEventStats stats)935 private static StatsEvent buildStatsEvent(UceEventStats stats) { 936 return TelephonyStatsLog.buildStatsEvent( 937 UCE_EVENT_STATS, 938 stats.carrierId, 939 stats.slotId, 940 stats.type, 941 stats.successful, 942 stats.commandCode, 943 stats.networkResponse, 944 stats.count); 945 } 946 buildStatsEvent(PresenceNotifyEvent stats)947 private static StatsEvent buildStatsEvent(PresenceNotifyEvent stats) { 948 return TelephonyStatsLog.buildStatsEvent( 949 PRESENCE_NOTIFY_EVENT, 950 stats.carrierId, 951 stats.slotId, 952 stats.reason, 953 stats.contentBodyReceived, 954 stats.rcsCapsCount, 955 stats.mmtelCapsCount, 956 stats.noCapsCount, 957 stats.count); 958 } 959 buildStatsEvent(GbaEvent stats)960 private static StatsEvent buildStatsEvent(GbaEvent stats) { 961 return TelephonyStatsLog.buildStatsEvent( 962 GBA_EVENT, 963 stats.carrierId, 964 stats.slotId, 965 stats.successful, 966 stats.failedReason, 967 stats.count); 968 } 969 970 /** Returns all phones in {@link PhoneFactory}, or an empty array if phones not made yet. */ getPhonesIfAny()971 private static Phone[] getPhonesIfAny() { 972 try { 973 return PhoneFactory.getPhones(); 974 } catch (IllegalStateException e) { 975 // Phones have not been made yet 976 return new Phone[0]; 977 } 978 } 979 980 /** Returns the value rounded to the bucket. */ round(long value, long bucket)981 private static long round(long value, long bucket) { 982 return bucket == 0 ? value : ((value + bucket / 2) / bucket) * bucket; 983 } 984 } 985