Merge "Only config VoNR per carrier config if card is PRESENT" into sc-v2-dev
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index 1701318..bb7d8d3 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -23,7 +23,7 @@
// Holds atoms to store on persist storage in case of power cycle or process crash.
// NOTE: using int64 rather than google.protobuf.Timestamp for timestamps simplifies implementation.
-// Next id: 22
+// Next id: 50
message PersistAtoms {
/* Aggregated RAT usage during the call. */
repeated VoiceCallRatUsage voice_call_rat_usage = 1;
@@ -93,6 +93,84 @@
/* Timestamp of last network_requests pull. */
optional int64 network_requests_pull_timestamp_millis = 23;
+
+ /* RCS single registrtions feature tag information. */
+ repeated ImsRegistrationFeatureTagStats ims_registration_feature_tag_stats = 24;
+
+ /* Timestamp of last ims_registration_feature_tag_stats pull. */
+ optional int64 ims_registration_feature_tag_stats_pull_timestamp_millis = 25;
+
+ /* RCS client provisioning statistics and information. */
+ repeated RcsClientProvisioningStats rcs_client_provisioning_stats = 26;
+
+ /* Timestamp of last rcs_client_provisioning_stats pull. */
+ optional int64 rcs_client_provisioning_stats_pull_timestamp_millis = 27;
+
+ /* RCS configuration statistics and information based ACS. */
+ repeated RcsAcsProvisioningStats rcs_acs_provisioning_stats = 28;
+
+ /* Timestamp of last rcs_acs_provisioning_stats pull. */
+ optional int64 rcs_acs_provisioning_stats_pull_timestamp_millis = 29;
+
+ /* SIP delegate statistics and information. */
+ repeated SipDelegateStats sip_delegate_stats = 30;
+
+ /* Timestamp of last sip_delegate_stats pull. */
+ optional int64 sip_delegate_stats_pull_timestamp_millis = 31;
+
+ /* SIP Transport featuere tag statistics and information. */
+ repeated SipTransportFeatureTagStats sip_transport_feature_tag_stats = 32;
+
+ /* Timestamp of last sip_transport_feature_tag_stats pull. */
+ optional int64 sip_transport_feature_tag_stats_pull_timestamp_millis = 33;
+
+ /* SIP Message response statistics and information. */
+ repeated SipMessageResponse sip_message_response = 34;
+
+ /* Timestamp of last sip_message_response pull. */
+ optional int64 sip_message_response_pull_timestamp_millis = 35;
+
+ /* SIP Transport session statistics and information. */
+ repeated SipTransportSession sip_transport_session = 36;
+
+ /* Timestamp of last sip_transport_session pull. */
+ optional int64 sip_transport_session_pull_timestamp_millis = 37;
+
+ /* Dedicated bearer listener statistics and information. */
+ repeated ImsDedicatedBearerListenerEvent ims_dedicated_bearer_listener_event = 38;
+
+ /* Timestamp of last ims_dedicated_bearer_listener_event pull. */
+ optional int64 ims_dedicated_bearer_listener_event_pull_timestamp_millis = 39;
+
+ /* Dedicated bearer event statistics and information. */
+ repeated ImsDedicatedBearerEvent ims_dedicated_bearer_event = 40;
+
+ /* Timestamp of last ims_dedicated_bearer_event pull. */
+ optional int64 ims_dedicated_bearer_event_pull_timestamp_millis = 41;
+
+ /* Publish featere tag statistics and information. */
+ repeated ImsRegistrationServiceDescStats ims_registration_service_desc_stats = 42;
+
+ /* Timestamp of last ims_registration_service_desc_stats pull. */
+ optional int64 ims_registration_service_desc_stats_pull_timestamp_millis = 43;
+
+ /* UCE event stats statistics and information. */
+ repeated UceEventStats uce_event_stats = 44;
+
+ /* Timestamp of last uce_event_stats pull. */
+ optional int64 uce_event_stats_pull_timestamp_millis = 45;
+
+ /* Presence notify event statistics and information. */
+ repeated PresenceNotifyEvent presence_notify_event = 46;
+
+ /* Timestamp of last presence_notify_event pull. */
+ optional int64 presence_notify_event_pull_timestamp_millis = 47;
+
+ /* GBA event statistics and information. */
+ repeated GbaEvent gba_event = 48;
+
+ /* Timestamp of last gba_event pull. */
+ optional int64 gba_event_pull_timestamp_millis = 49;
}
// The canonical versions of the following enums live in:
@@ -273,3 +351,127 @@
optional int32 enterprise_request_count = 2;
optional int32 enterprise_release_count = 3;
}
+
+message ImsRegistrationFeatureTagStats {
+ optional int32 carrier_id = 1;
+ optional int32 slot_id = 2;
+ optional int32 feature_tag_name = 3;
+ optional int32 registration_tech = 4;
+ optional int64 registered_millis = 5;
+}
+
+message RcsClientProvisioningStats {
+ optional int32 carrier_id = 1;
+ optional int32 slot_id = 2;
+ optional int32 event = 3;
+ optional int32 count = 4;
+}
+
+message RcsAcsProvisioningStats {
+ optional int32 carrier_id = 1;
+ optional int32 slot_id = 2;
+ optional int32 response_code = 3;
+ optional int32 response_type = 4;
+ optional bool is_single_registration_enabled = 5;
+ optional int32 count = 6;
+ optional int64 state_timer_millis = 7;
+}
+
+message SipDelegateStats {
+ optional int32 dimension = 1;
+ optional int32 carrier_id = 2;
+ optional int32 slot_id = 3;
+ optional int32 destroy_reason = 4;
+ optional int64 uptime_millis = 5;
+}
+
+message SipTransportFeatureTagStats {
+ optional int32 carrier_id = 1;
+ optional int32 slot_id = 2;
+ optional int32 feature_tag_name = 3;
+ optional int32 sip_transport_denied_reason = 4;
+ optional int32 sip_transport_deregistered_reason = 5;
+ optional int64 associated_millis = 6;
+}
+
+message SipMessageResponse {
+ optional int32 carrier_id = 1;
+ optional int32 slot_id = 2;
+ optional int32 sip_message_method = 3;
+ optional int32 sip_message_response = 4;
+ optional int32 sip_message_direction = 5;
+ optional int32 message_error = 6;
+ optional int32 count = 7;
+}
+
+message SipTransportSession {
+ optional int32 carrier_id = 1;
+ optional int32 slot_id = 2;
+ optional int32 session_method = 3;
+ optional int32 sip_message_direction = 4;
+ optional int32 sip_response = 5;
+ optional int32 session_count = 6;
+ optional int32 ended_gracefully_count = 7;
+
+ // Internal use only
+ optional bool is_ended_gracefully = 10001;
+}
+
+message ImsDedicatedBearerListenerEvent {
+ optional int32 carrier_id = 1;
+ optional int32 slot_id = 2;
+ optional int32 rat_at_end = 3;
+ optional int32 qci = 4;
+ optional bool dedicated_bearer_established = 5;
+ optional int32 event_count = 6;
+}
+
+message ImsDedicatedBearerEvent {
+ optional int32 carrier_id = 1;
+ optional int32 slot_id = 2;
+ optional int32 rat_at_end = 3;
+ optional int32 qci = 4;
+ optional int32 bearer_state = 5;
+ optional bool local_connection_info_received = 6;
+ optional bool remote_connection_info_received = 7;
+ optional bool has_listeners = 8;
+ optional int32 count = 9;
+}
+
+message ImsRegistrationServiceDescStats {
+ optional int32 carrier_id = 1;
+ optional int32 slot_id = 2;
+ optional int32 service_id_name = 3;
+ optional float service_id_version = 4;
+ optional int32 registration_tech = 5;
+ optional int64 published_millis = 6;
+}
+
+message UceEventStats {
+ optional int32 carrier_id = 1;
+ optional int32 slot_id = 2;
+ optional int32 type = 3;
+ optional bool successful = 4;
+ optional int32 command_code = 5;
+ optional int32 network_response = 6;
+ optional int32 count = 7;
+}
+
+message PresenceNotifyEvent {
+ optional int32 carrier_id = 1;
+ optional int32 slot_id = 2;
+ optional int32 reason = 3;
+ optional bool content_body_received = 4;
+ optional int32 rcs_caps_count = 5;
+ optional int32 mmtel_caps_count = 6;
+ optional int32 no_caps_count = 7;
+ optional int32 count = 8;
+}
+
+message GbaEvent {
+ optional int32 carrier_id = 1;
+ optional int32 slot_id = 2;
+ optional bool successful = 3;
+ optional int32 failed_reason = 4;
+ optional int32 count = 5;
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/GbaManager.java b/src/java/com/android/internal/telephony/GbaManager.java
index d6c59ea..b1db1ac 100644
--- a/src/java/com/android/internal/telephony/GbaManager.java
+++ b/src/java/com/android/internal/telephony/GbaManager.java
@@ -37,6 +37,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.metrics.RcsStats;
import com.android.telephony.Rlog;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -60,6 +61,7 @@
public static final int MAX_RETRY = 5;
@VisibleForTesting
public static final int REQUEST_TIMEOUT_MS = 5000;
+ private final RcsStats mRcsStats;
private final String mLogTag;
private final Context mContext;
@@ -191,7 +193,8 @@
}
@VisibleForTesting
- public GbaManager(Context context, int subId, String servicePackageName, int releaseTime) {
+ public GbaManager(Context context, int subId, String servicePackageName, int releaseTime,
+ RcsStats rcsStats) {
mContext = context;
mSubId = subId;
mLogTag = "GbaManager[" + subId + "]";
@@ -206,6 +209,7 @@
if (mReleaseTime < 0) {
mHandler.sendEmptyMessage(EVENT_BIND_SERVICE);
}
+ mRcsStats = rcsStats;
}
/**
@@ -213,7 +217,8 @@
*/
public static GbaManager make(Context context, int subId,
String servicePackageName, int releaseTime) {
- GbaManager gm = new GbaManager(context, subId, servicePackageName, releaseTime);
+ GbaManager gm = new GbaManager(context, subId, servicePackageName, releaseTime,
+ RcsStats.getInstance());
synchronized (sGbaManagers) {
sGbaManagers.put(subId, gm);
}
@@ -267,6 +272,7 @@
if (cb != null) {
try {
cb.onKeysAvailable(token, gbaKey, btId);
+ mRcsStats.onGbaSuccessEvent(mSubId);
} catch (RemoteException exception) {
logd("RemoteException " + exception);
}
@@ -291,6 +297,7 @@
if (cb != null) {
try {
cb.onAuthenticationFailure(token, reason);
+ mRcsStats.onGbaFailureEvent(mSubId, reason);
} catch (RemoteException exception) {
logd("RemoteException " + exception);
}
diff --git a/src/java/com/android/internal/telephony/NetworkTypeController.java b/src/java/com/android/internal/telephony/NetworkTypeController.java
index e8ad6ff..92f277c 100644
--- a/src/java/com/android/internal/telephony/NetworkTypeController.java
+++ b/src/java/com/android/internal/telephony/NetworkTypeController.java
@@ -1010,6 +1010,12 @@
}
private void updateTimers() {
+ if ((mPhone.getCachedAllowedNetworkTypesBitmask()
+ & TelephonyManager.NETWORK_TYPE_BITMASK_NR) == 0) {
+ resetAllTimers();
+ return;
+ }
+
String currentState = getCurrentState().getName();
if (mIsPrimaryTimerActive && getOverrideNetworkType() == getCurrentOverrideNetworkType()) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java b/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
index 32b2f7b..7d7f065 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java
@@ -118,7 +118,7 @@
} else {
loge("The connection does not have a valid link properties.");
}
- mQosCallbackTracker = new QosCallbackTracker(this);
+ mQosCallbackTracker = new QosCallbackTracker(this, mPhone.getPhoneId());
}
private @NetworkType int getNetworkType() {
diff --git a/src/java/com/android/internal/telephony/dataconnection/QosCallbackTracker.java b/src/java/com/android/internal/telephony/dataconnection/QosCallbackTracker.java
index 795ed14..e4b14e8 100644
--- a/src/java/com/android/internal/telephony/dataconnection/QosCallbackTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/QosCallbackTracker.java
@@ -17,22 +17,25 @@
package com.android.internal.telephony.dataconnection;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.net.LinkAddress;
import android.net.QosSession;
+import android.telephony.TelephonyProtoEnums;
+import android.telephony.data.EpsBearerQosSessionAttributes;
import android.telephony.data.EpsQos;
import android.telephony.data.NrQos;
-import android.telephony.data.EpsBearerQosSessionAttributes;
import android.telephony.data.NrQosSessionAttributes;
import android.telephony.data.QosBearerFilter;
import android.telephony.data.QosBearerSession;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.metrics.RcsStats;
import com.android.telephony.Rlog;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -44,21 +47,48 @@
* {@hide}
*/
public class QosCallbackTracker {
+ private static final int DedicatedBearerEvent_STATE_NONE = 0;
+ private static final int DedicatedBearerEvent_STATE_ADDED = 1;
+ private static final int DedicatedBearerEvent_STATE_MODIFIED = 2;
+ private static final int DedicatedBearerEvent_STATE_DELETED = 3;
+
@NonNull private final String mTag;
@NonNull private final DcNetworkAgent mDcNetworkAgent;
@NonNull private final Map<Integer, QosBearerSession> mQosBearerSessions;
+ @NonNull private final RcsStats mRcsStats;
// We perform an exact match on the address
@NonNull private final Map<Integer, IFilter> mCallbacksToFilter;
+ @NonNull private final int mPhoneID;
+
/**
* Construct a new tracker
* @param dcNetworkAgent the network agent to send events to
*/
- public QosCallbackTracker(@NonNull final DcNetworkAgent dcNetworkAgent) {
+ public QosCallbackTracker(@NonNull final DcNetworkAgent dcNetworkAgent,
+ @NonNull final int phoneID) {
mQosBearerSessions = new HashMap<>();
mCallbacksToFilter = new HashMap<>();
mDcNetworkAgent = dcNetworkAgent;
+ mPhoneID = phoneID;
+ mRcsStats = RcsStats.getInstance();
+ mTag = "QosCallbackTracker" + "-" + mDcNetworkAgent.getNetwork().getNetId();
+ }
+
+ /**
+ * Construct a new tracker
+ * @param dcNetworkAgent the network agent to send events to
+ * @param rcsStats metrics package to store QoS event info
+ */
+ @VisibleForTesting
+ public QosCallbackTracker(@NonNull final DcNetworkAgent dcNetworkAgent,
+ @NonNull final int phoneID, @NonNull final RcsStats rcsStats) {
+ mQosBearerSessions = new HashMap<>();
+ mCallbacksToFilter = new HashMap<>();
+ mDcNetworkAgent = dcNetworkAgent;
+ mPhoneID = phoneID;
+ mRcsStats = rcsStats;
mTag = "QosCallbackTracker" + "-" + mDcNetworkAgent.getNetwork().getNetId();
}
@@ -77,6 +107,8 @@
for (final QosBearerSession session : mQosBearerSessions.values()) {
if (doFiltersMatch(session, filter)) {
sendSessionAvailable(callbackId, session, filter);
+
+ notifyMetricDedicatedBearerListenerAdded(callbackId, session);
}
}
}
@@ -89,6 +121,7 @@
public void removeFilter(final int callbackId) {
logd("removeFilter: callbackId=" + callbackId);
mCallbacksToFilter.remove(callbackId);
+ notifyMetricDedicatedBearerListenerRemoved(callbackId);
}
/**
@@ -98,13 +131,18 @@
*/
public void updateSessions(@NonNull final List<QosBearerSession> sessions) {
logd("updateSessions: sessions size=" + sessions.size());
+
+ int bearerState = DedicatedBearerEvent_STATE_NONE;
+
final List<QosBearerSession> sessionsToAdd = new ArrayList<>();
final Map<Integer, QosBearerSession> incomingSessions = new HashMap<>();
+ final HashSet<Integer> sessionsReportedToMetric = new HashSet<>();
for (final QosBearerSession incomingSession : sessions) {
- incomingSessions.put(incomingSession.getQosBearerSessionId(), incomingSession);
+ int sessionId = incomingSession.getQosBearerSessionId();
+ incomingSessions.put(sessionId, incomingSession);
final QosBearerSession existingSession = mQosBearerSessions.get(
- incomingSession.getQosBearerSessionId());
+ sessionId);
for (final int callbackId : mCallbacksToFilter.keySet()) {
final IFilter filter = mCallbacksToFilter.get(callbackId);
@@ -115,6 +153,7 @@
if (!existingSessionMatch && incomingSessionMatch) {
// The filter matches now and didn't match earlier
sendSessionAvailable(callbackId, incomingSession, filter);
+ bearerState = DedicatedBearerEvent_STATE_ADDED;
}
if (existingSessionMatch && incomingSessionMatch) {
@@ -122,24 +161,50 @@
// the callback still needs to be notified
if (!incomingSession.getQos().equals(existingSession.getQos())) {
sendSessionAvailable(callbackId, incomingSession, filter);
+ bearerState = DedicatedBearerEvent_STATE_MODIFIED;
}
}
+
+ // this QosBearerSession has registered QosCallbackId
+ if (!sessionsReportedToMetric.contains(sessionId) && incomingSessionMatch) {
+ // this session has listener
+ notifyMetricDedicatedBearerEvent(incomingSession, bearerState, true);
+ sessionsReportedToMetric.add(sessionId);
+ }
+ }
+
+ // this QosBearerSession does not have registered QosCallbackId
+ if (!sessionsReportedToMetric.contains(sessionId)) {
+ // no listener is registered to this session
+ bearerState = DedicatedBearerEvent_STATE_ADDED;
+ notifyMetricDedicatedBearerEvent(incomingSession, bearerState, false);
+ sessionsReportedToMetric.add(sessionId);
}
sessionsToAdd.add(incomingSession);
}
final List<Integer> sessionsToRemove = new ArrayList<>();
+ sessionsReportedToMetric.clear();
+ bearerState = DedicatedBearerEvent_STATE_DELETED;
+
// Find sessions that no longer exist
for (final QosBearerSession existingSession : mQosBearerSessions.values()) {
- if (!incomingSessions.containsKey(existingSession.getQosBearerSessionId())) {
+ final int sessionId = existingSession.getQosBearerSessionId();
+ if (!incomingSessions.containsKey(sessionId)) {
for (final int callbackId : mCallbacksToFilter.keySet()) {
final IFilter filter = mCallbacksToFilter.get(callbackId);
// The filter matches which means it was previously available, and now is lost
if (doFiltersMatch(existingSession, filter)) {
sendSessionLost(callbackId, existingSession);
+ notifyMetricDedicatedBearerEvent(existingSession, bearerState, true);
+ sessionsReportedToMetric.add(sessionId);
}
}
sessionsToRemove.add(existingSession.getQosBearerSessionId());
+ if (!sessionsReportedToMetric.contains(sessionId)) {
+ notifyMetricDedicatedBearerEvent(existingSession, bearerState, false);
+ sessionsReportedToMetric.add(sessionId);
+ }
}
}
@@ -161,7 +226,8 @@
private boolean matchesByLocalAddress(
QosBearerFilter sessionFilter, final IFilter filter) {
- for (final LinkAddress qosAddress : sessionFilter.getLocalAddresses()) {
+ if (sessionFilter.getLocalPortRange() == null) return false;
+ for (final LinkAddress qosAddress: sessionFilter.getLocalAddresses()) {
return filter.matchesLocalAddress(qosAddress.getAddress(),
sessionFilter.getLocalPortRange().getStart(),
sessionFilter.getLocalPortRange().getEnd());
@@ -171,6 +237,7 @@
private boolean matchesByRemoteAddress(
QosBearerFilter sessionFilter, final IFilter filter) {
+ if (sessionFilter.getRemotePortRange() == null) return false;
for (final LinkAddress qosAddress : sessionFilter.getRemoteAddresses()) {
return filter.matchesRemoteAddress(qosAddress.getAddress(),
sessionFilter.getRemotePortRange().getStart(),
@@ -181,6 +248,8 @@
private boolean matchesByRemoteAndLocalAddress(
QosBearerFilter sessionFilter, final IFilter filter) {
+ if (sessionFilter.getLocalPortRange() == null
+ || sessionFilter.getRemotePortRange() == null) return false;
for (final LinkAddress remoteAddress : sessionFilter.getRemoteAddresses()) {
for (final LinkAddress localAddress : sessionFilter.getLocalAddresses()) {
return filter.matchesRemoteAddress(remoteAddress.getAddress(),
@@ -208,17 +277,21 @@
for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) {
if (!sessionFilter.getLocalAddresses().isEmpty()
&& !sessionFilter.getRemoteAddresses().isEmpty()
+ && sessionFilter.getLocalPortRange() != null
&& sessionFilter.getLocalPortRange().isValid()
+ && sessionFilter.getRemotePortRange() != null
&& sessionFilter.getRemotePortRange().isValid()) {
if (matchesByRemoteAndLocalAddress(sessionFilter, filter)) {
qosFilter = getFilterByPrecedence(qosFilter, sessionFilter);
}
} else if (!sessionFilter.getRemoteAddresses().isEmpty()
+ && sessionFilter.getRemotePortRange() != null
&& sessionFilter.getRemotePortRange().isValid()) {
if (matchesByRemoteAddress(sessionFilter, filter)) {
qosFilter = getFilterByPrecedence(qosFilter, sessionFilter);
}
} else if (!sessionFilter.getLocalAddresses().isEmpty()
+ && sessionFilter.getLocalPortRange() != null
&& sessionFilter.getLocalPortRange().isValid()) {
if (matchesByLocalAddress(sessionFilter, filter)) {
qosFilter = getFilterByPrecedence(qosFilter, sessionFilter);
@@ -232,7 +305,8 @@
@NonNull final QosBearerSession session, @NonNull IFilter filter) {
QosBearerFilter qosBearerFilter = getMatchingQosBearerFilter(session, filter);
List<InetSocketAddress> remoteAddresses = new ArrayList<>();
- if(qosBearerFilter.getRemoteAddresses().size() > 0) {
+ if (qosBearerFilter.getRemoteAddresses().size() > 0
+ && qosBearerFilter.getRemotePortRange() != null) {
remoteAddresses.add(
new InetSocketAddress(qosBearerFilter.getRemoteAddresses().get(0).getAddress(),
qosBearerFilter.getRemotePortRange().getStart()));
@@ -262,6 +336,9 @@
callbackId, session.getQosBearerSessionId(), nrQosAttr);
}
+ /** added to notify to Metric for passing DedicatedBearerEstablished info */
+ notifyMetricDedicatedBearerListenerBearerUpdateSession(callbackId, session);
+
logd("sendSessionAvailable, callbackId=" + callbackId);
}
@@ -272,6 +349,85 @@
logd("sendSessionLost, callbackId=" + callbackId);
}
+ private void notifyMetricDedicatedBearerListenerAdded(
+ final int callbackId, final QosBearerSession session) {
+
+ final int slotId = mPhoneID;
+ final int rat = getRatInfoFromSessionInfo(session);
+ final int qci = getQCIFromSessionInfo(session);
+
+ mRcsStats.onImsDedicatedBearerListenerAdded(callbackId, slotId, rat, qci);
+ }
+
+ private void notifyMetricDedicatedBearerListenerBearerUpdateSession(
+ final int callbackId, final QosBearerSession session) {
+
+ final int slotId = mPhoneID;
+ final int rat = getRatInfoFromSessionInfo(session);
+ final int qci = getQCIFromSessionInfo(session);
+
+ mRcsStats.onImsDedicatedBearerListenerUpdateSession(callbackId, slotId, rat, qci, true);
+ }
+
+ private void notifyMetricDedicatedBearerListenerRemoved(final int callbackId) {
+ mRcsStats.onImsDedicatedBearerListenerRemoved(callbackId);
+ }
+
+ private int getQCIFromSessionInfo(final QosBearerSession session) {
+ if (session.getQos() instanceof EpsQos) {
+ return ((EpsQos) session.getQos()).getQci();
+ } else if (session.getQos() instanceof NrQos) {
+ return ((NrQos) session.getQos()).get5Qi();
+ }
+
+ return 0;
+ }
+
+ private int getRatInfoFromSessionInfo(final QosBearerSession session) {
+ if (session.getQos() instanceof EpsQos) {
+ return TelephonyProtoEnums.NETWORK_TYPE_LTE;
+ } else if (session.getQos() instanceof NrQos) {
+ return TelephonyProtoEnums.NETWORK_TYPE_NR;
+ }
+
+ return 0;
+ }
+
+ private boolean doesLocalConnectionInfoExist(final QosBearerSession qosBearerSession) {
+ for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) {
+ if (!sessionFilter.getLocalAddresses().isEmpty()
+ && sessionFilter.getLocalPortRange() != null
+ && sessionFilter.getLocalPortRange().isValid()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean doesRemoteConnectionInfoExist(final QosBearerSession qosBearerSession) {
+ for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) {
+ if (!sessionFilter.getRemoteAddresses().isEmpty()
+ && sessionFilter.getRemotePortRange() != null
+ && sessionFilter.getRemotePortRange().isValid()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void notifyMetricDedicatedBearerEvent(final QosBearerSession session,
+ final int bearerState, final boolean hasListener) {
+
+ final int slotId = mPhoneID;
+ int ratAtEnd = getRatInfoFromSessionInfo(session);
+ int qci = getQCIFromSessionInfo(session);
+ boolean localConnectionInfoReceived = doesLocalConnectionInfoExist(session);
+ boolean remoteConnectionInfoReceived = doesRemoteConnectionInfoExist(session);
+
+ mRcsStats.onImsDedicatedBearerEvent(slotId, ratAtEnd, qci, bearerState,
+ localConnectionInfoReceived, remoteConnectionInfoReceived, hasListener);
+ }
+
public interface IFilter {
public boolean matchesLocalAddress(InetAddress address, int startPort, int endPort);
public boolean matchesRemoteAddress(InetAddress address, int startPort, int endPort);
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index 6e54230..40e32e2 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -24,13 +24,26 @@
import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_DATA_SERVICE_SWITCH;
import static com.android.internal.telephony.TelephonyStatsLog.CELLULAR_SERVICE_STATE;
import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION;
+import static com.android.internal.telephony.TelephonyStatsLog.GBA_EVENT;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_DEDICATED_BEARER_EVENT;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_DEDICATED_BEARER_LISTENER_EVENT;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_FEATURE_TAG_STATS;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS;
import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_STATS;
import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_TERMINATION;
import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS;
import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS;
+import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS;
import static com.android.internal.telephony.TelephonyStatsLog.SIM_SLOT_STATE;
+import static com.android.internal.telephony.TelephonyStatsLog.SIP_DELEGATE_STATS;
+import static com.android.internal.telephony.TelephonyStatsLog.SIP_MESSAGE_RESPONSE;
+import static com.android.internal.telephony.TelephonyStatsLog.SIP_TRANSPORT_FEATURE_TAG_STATS;
+import static com.android.internal.telephony.TelephonyStatsLog.SIP_TRANSPORT_SESSION;
import static com.android.internal.telephony.TelephonyStatsLog.SUPPORTED_RADIO_ACCESS_FAMILY;
import static com.android.internal.telephony.TelephonyStatsLog.TELEPHONY_NETWORK_REQUESTS;
+import static com.android.internal.telephony.TelephonyStatsLog.UCE_EVENT_STATS;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION;
@@ -47,11 +60,24 @@
import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.GbaEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerListenerEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationFeatureTagStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationServiceDescStats;
import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats;
import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms;
import com.android.internal.telephony.nano.PersistAtomsProto.NetworkRequests;
import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
+import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.RcsClientProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipDelegateStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipMessageResponse;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportFeatureTagStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.UceEventStats;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
import com.android.internal.util.ConcurrentUtils;
@@ -124,6 +150,19 @@
registerAtom(IMS_REGISTRATION_STATS, POLICY_PULL_DAILY);
registerAtom(IMS_REGISTRATION_TERMINATION, POLICY_PULL_DAILY);
registerAtom(TELEPHONY_NETWORK_REQUESTS, POLICY_PULL_DAILY);
+ registerAtom(IMS_REGISTRATION_FEATURE_TAG_STATS, POLICY_PULL_DAILY);
+ registerAtom(RCS_CLIENT_PROVISIONING_STATS, POLICY_PULL_DAILY);
+ registerAtom(RCS_ACS_PROVISIONING_STATS, POLICY_PULL_DAILY);
+ registerAtom(SIP_DELEGATE_STATS, POLICY_PULL_DAILY);
+ registerAtom(SIP_TRANSPORT_FEATURE_TAG_STATS, POLICY_PULL_DAILY);
+ registerAtom(SIP_MESSAGE_RESPONSE, POLICY_PULL_DAILY);
+ registerAtom(SIP_TRANSPORT_SESSION, POLICY_PULL_DAILY);
+ registerAtom(IMS_DEDICATED_BEARER_LISTENER_EVENT, POLICY_PULL_DAILY);
+ registerAtom(IMS_DEDICATED_BEARER_EVENT, POLICY_PULL_DAILY);
+ registerAtom(IMS_REGISTRATION_SERVICE_DESC_STATS, POLICY_PULL_DAILY);
+ registerAtom(UCE_EVENT_STATS, POLICY_PULL_DAILY);
+ registerAtom(PRESENCE_NOTIFY_EVENT, POLICY_PULL_DAILY);
+ registerAtom(GBA_EVENT, POLICY_PULL_DAILY);
Rlog.d(TAG, "registered");
} else {
@@ -175,6 +214,32 @@
return pullImsRegistrationTermination(data);
case TELEPHONY_NETWORK_REQUESTS:
return pullTelephonyNetworkRequests(data);
+ case IMS_REGISTRATION_FEATURE_TAG_STATS:
+ return pullImsRegistrationFeatureTagStats(data);
+ case RCS_CLIENT_PROVISIONING_STATS:
+ return pullRcsClientProvisioningStats(data);
+ case RCS_ACS_PROVISIONING_STATS:
+ return pullRcsAcsProvisioningStats(data);
+ case SIP_DELEGATE_STATS:
+ return pullSipDelegateStats(data);
+ case SIP_TRANSPORT_FEATURE_TAG_STATS:
+ return pullSipTransportFeatureTagStats(data);
+ case SIP_MESSAGE_RESPONSE:
+ return pullSipMessageResponse(data);
+ case SIP_TRANSPORT_SESSION:
+ return pullSipTransportSession(data);
+ case IMS_DEDICATED_BEARER_LISTENER_EVENT:
+ return pullImsDedicatedBearerListenerEvent(data);
+ case IMS_DEDICATED_BEARER_EVENT:
+ return pullImsDedicatedBearerEvent(data);
+ case IMS_REGISTRATION_SERVICE_DESC_STATS:
+ return pullImsRegistrationServiceDescStats(data);
+ case UCE_EVENT_STATS:
+ return pullUceEventStats(data);
+ case PRESENCE_NOTIFY_EVENT:
+ return pullPresenceNotifyEvent(data);
+ case GBA_EVENT:
+ return pullGbaEvent(data);
default:
Rlog.e(TAG, String.format("unexpected atom ID %d", atomTag));
return StatsManager.PULL_SKIP;
@@ -401,6 +466,179 @@
}
}
+ private int pullImsRegistrationFeatureTagStats(List<StatsEvent> data) {
+ RcsStats.getInstance().onFlushIncompleteImsRegistrationFeatureTagStats();
+
+ ImsRegistrationFeatureTagStats[] persistAtoms =
+ mStorage.getImsRegistrationFeatureTagStats(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "IMS_REGISTRATION_FEATURE_TAG_STATS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullRcsClientProvisioningStats(List<StatsEvent> data) {
+ RcsClientProvisioningStats[] persistAtoms =
+ mStorage.getRcsClientProvisioningStats(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "RCS_CLIENT_PROVISIONING_STATS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullRcsAcsProvisioningStats(List<StatsEvent> data) {
+ RcsStats.getInstance().onFlushIncompleteRcsAcsProvisioningStats();
+
+ RcsAcsProvisioningStats[] persistAtoms =
+ mStorage.getRcsAcsProvisioningStats(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "RCS_ACS_PROVISIONING_STATS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullSipDelegateStats(List<StatsEvent> data) {
+ SipDelegateStats[] persisAtoms =
+ mStorage.getSipDelegateStats(MIN_COOLDOWN_MILLIS);
+ if (persisAtoms != null) {
+ Arrays.stream(persisAtoms)
+ .forEach(persisAtom -> data.add(buildStatsEvent(persisAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "SIP_DELEGATE_STATS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullSipTransportFeatureTagStats(List<StatsEvent> data) {
+ RcsStats.getInstance().concludeSipTransportFeatureTagsStat();
+
+ SipTransportFeatureTagStats[] persisAtoms =
+ mStorage.getSipTransportFeatureTagStats(MIN_COOLDOWN_MILLIS);
+ if (persisAtoms != null) {
+ Arrays.stream(persisAtoms)
+ .forEach(persisAtom -> data.add(buildStatsEvent(persisAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "SIP_DELEGATE_STATS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullSipMessageResponse(List<StatsEvent> data) {
+ SipMessageResponse[] persistAtoms =
+ mStorage.getSipMessageResponse(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "RCS_SIP_MESSAGE_RESPONSE pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullSipTransportSession(List<StatsEvent> data) {
+ SipTransportSession[] persistAtoms =
+ mStorage.getSipTransportSession(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "RCS_SIP_TRANSPORT_SESSION pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullImsDedicatedBearerListenerEvent(List<StatsEvent> data) {
+ ImsDedicatedBearerListenerEvent[] persistAtoms =
+ mStorage.getImsDedicatedBearerListenerEvent(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "IMS_DEDICATED_BEARER_LISTENER_EVENT pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullImsDedicatedBearerEvent(List<StatsEvent> data) {
+ ImsDedicatedBearerEvent[] persistAtoms =
+ mStorage.getImsDedicatedBearerEvent(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "IMS_DEDICATED_BEARER_EVENT pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullImsRegistrationServiceDescStats(List<StatsEvent> data) {
+ RcsStats.getInstance().onFlushIncompleteImsRegistrationServiceDescStats();
+ ImsRegistrationServiceDescStats[] persistAtoms =
+ mStorage.getImsRegistrationServiceDescStats(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "IMS_REGISTRATION_SERVICE_DESC_STATS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullUceEventStats(List<StatsEvent> data) {
+ UceEventStats[] persistAtoms = mStorage.getUceEventStats(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "UCE_EVENT_STATS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullPresenceNotifyEvent(List<StatsEvent> data) {
+ PresenceNotifyEvent[] persistAtoms = mStorage.getPresenceNotifyEvent(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "PRESENCE_NOTIFY_EVENT pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullGbaEvent(List<StatsEvent> data) {
+ GbaEvent[] persistAtoms = mStorage.getGbaEvent(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "GBA_EVENT pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
/** Registers a pulled atom ID {@code atomId} with optional {@code policy} for pulling. */
private void registerAtom(int atomId, @Nullable StatsManager.PullAtomMetadata policy) {
mStatsManager.setPullAtomCallback(atomId, policy, ConcurrentUtils.DIRECT_EXECUTOR, this);
@@ -582,6 +820,153 @@
networkRequests.enterpriseReleaseCount);
}
+ private static StatsEvent buildStatsEvent(ImsRegistrationFeatureTagStats stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ IMS_REGISTRATION_FEATURE_TAG_STATS,
+ stats.carrierId,
+ stats.slotId,
+ stats.featureTagName,
+ stats.registrationTech,
+ (int) (round(stats.registeredMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+ }
+
+ private static StatsEvent buildStatsEvent(RcsClientProvisioningStats stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ RCS_CLIENT_PROVISIONING_STATS,
+ stats.carrierId,
+ stats.slotId,
+ stats.event,
+ stats.count);
+ }
+
+ private static StatsEvent buildStatsEvent(RcsAcsProvisioningStats stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ RCS_ACS_PROVISIONING_STATS,
+ stats.carrierId,
+ stats.slotId,
+ stats.responseCode,
+ stats.responseType,
+ stats.isSingleRegistrationEnabled,
+ stats.count,
+ (int) (round(stats.stateTimerMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+ }
+
+ private static StatsEvent buildStatsEvent(SipDelegateStats stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ SIP_DELEGATE_STATS,
+ stats.dimension,
+ stats.carrierId,
+ stats.slotId,
+ (int) (round(stats.uptimeMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS),
+ stats.destroyReason);
+ }
+
+ private static StatsEvent buildStatsEvent(SipTransportFeatureTagStats stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ SIP_TRANSPORT_FEATURE_TAG_STATS,
+ stats.carrierId,
+ stats.slotId,
+ stats.featureTagName,
+ stats.sipTransportDeniedReason,
+ stats.sipTransportDeregisteredReason,
+ (int) (round(stats.associatedMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+ }
+
+ private static StatsEvent buildStatsEvent(SipMessageResponse stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ SIP_MESSAGE_RESPONSE,
+ stats.carrierId,
+ stats.slotId,
+ stats.sipMessageMethod,
+ stats.sipMessageResponse,
+ stats.sipMessageDirection,
+ stats.messageError,
+ stats.count);
+ }
+
+ private static StatsEvent buildStatsEvent(SipTransportSession stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ SIP_TRANSPORT_SESSION,
+ stats.carrierId,
+ stats.slotId,
+ stats.sessionMethod,
+ stats.sipMessageDirection,
+ stats.sipResponse,
+ stats.sessionCount,
+ stats.endedGracefullyCount);
+ }
+
+ private static StatsEvent buildStatsEvent(ImsDedicatedBearerListenerEvent stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ IMS_DEDICATED_BEARER_LISTENER_EVENT,
+ stats.carrierId,
+ stats.slotId,
+ stats.ratAtEnd,
+ stats.qci,
+ stats.dedicatedBearerEstablished,
+ stats.eventCount);
+ }
+
+ private static StatsEvent buildStatsEvent(ImsDedicatedBearerEvent stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ IMS_DEDICATED_BEARER_EVENT,
+ stats.carrierId,
+ stats.slotId,
+ stats.ratAtEnd,
+ stats.qci,
+ stats.bearerState,
+ stats.localConnectionInfoReceived,
+ stats.remoteConnectionInfoReceived,
+ stats.hasListeners,
+ stats.count);
+ }
+
+ private static StatsEvent buildStatsEvent(ImsRegistrationServiceDescStats stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ IMS_REGISTRATION_SERVICE_DESC_STATS,
+ stats.carrierId,
+ stats.slotId,
+ stats.serviceIdName,
+ stats.serviceIdVersion,
+ stats.registrationTech,
+ (int) (round(stats.publishedMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
+ }
+
+ private static StatsEvent buildStatsEvent(UceEventStats stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ UCE_EVENT_STATS,
+ stats.carrierId,
+ stats.slotId,
+ stats.type,
+ stats.successful,
+ stats.commandCode,
+ stats.networkResponse,
+ stats.count);
+ }
+
+ private static StatsEvent buildStatsEvent(PresenceNotifyEvent stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ PRESENCE_NOTIFY_EVENT,
+ stats.carrierId,
+ stats.slotId,
+ stats.reason,
+ stats.contentBodyReceived,
+ stats.rcsCapsCount,
+ stats.mmtelCapsCount,
+ stats.noCapsCount,
+ stats.count);
+ }
+
+ private static StatsEvent buildStatsEvent(GbaEvent stats) {
+ return TelephonyStatsLog.buildStatsEvent(
+ GBA_EVENT,
+ stats.carrierId,
+ stats.slotId,
+ stats.successful,
+ stats.failedReason,
+ stats.count);
+ }
+
/** Returns all phones in {@link PhoneFactory}, or an empty array if phones not made yet. */
private static Phone[] getPhonesIfAny() {
try {
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index 6bdceed..a93fb0e 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -28,12 +28,25 @@
import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.GbaEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerListenerEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationFeatureTagStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationServiceDescStats;
import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats;
import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms;
import com.android.internal.telephony.nano.PersistAtomsProto.NetworkRequests;
import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms;
+import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.RcsClientProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipDelegateStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipMessageResponse;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportFeatureTagStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.UceEventStats;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
import com.android.internal.util.ArrayUtils;
@@ -100,6 +113,45 @@
/** Maximum number of IMS registration terminations to store between pulls. */
private static final int MAX_NUM_IMS_REGISTRATION_TERMINATIONS = 10;
+ /** Maximum number of IMS Registration Feature Tags to store between pulls. */
+ private static final int MAX_NUM_IMS_REGISTRATION_FEATURE_STATS = 25;
+
+ /** Maximum number of RCS Client Provisioning to store between pulls. */
+ private static final int MAX_NUM_RCS_CLIENT_PROVISIONING_STATS = 10;
+
+ /** Maximum number of RCS Acs Provisioning to store between pulls. */
+ private static final int MAX_NUM_RCS_ACS_PROVISIONING_STATS = 10;
+
+ /** Maximum number of Sip Message Response to store between pulls. */
+ private static final int MAX_NUM_SIP_MESSAGE_RESPONSE_STATS = 25;
+
+ /** Maximum number of Sip Transport Session to store between pulls. */
+ private static final int MAX_NUM_SIP_TRANSPORT_SESSION_STATS = 25;
+
+ /** Maximum number of Sip Delegate to store between pulls. */
+ private static final int MAX_NUM_SIP_DELEGATE_STATS = 10;
+
+ /** Maximum number of Sip Transport Feature Tag to store between pulls. */
+ private static final int MAX_NUM_SIP_TRANSPORT_FEATURE_TAG_STATS = 25;
+
+ /** Maximum number of Dedicated Bearer Listener Event to store between pulls. */
+ private static final int MAX_NUM_DEDICATED_BEARER_LISTENER_EVENT_STATS = 10;
+
+ /** Maximum number of Dedicated Bearer Event to store between pulls. */
+ private static final int MAX_NUM_DEDICATED_BEARER_EVENT_STATS = 10;
+
+ /** Maximum number of IMS Registration Service Desc to store between pulls. */
+ private static final int MAX_NUM_IMS_REGISTRATION_SERVICE_DESC_STATS = 25;
+
+ /** Maximum number of UCE Event to store between pulls. */
+ private static final int MAX_NUM_UCE_EVENT_STATS = 25;
+
+ /** Maximum number of Presence Notify Event to store between pulls. */
+ private static final int MAX_NUM_PRESENCE_NOTIFY_EVENT_STATS = 50;
+
+ /** Maximum number of GBA Event to store between pulls. */
+ private static final int MAX_NUM_GBA_EVENT_STATS = 10;
+
/** Stores persist atoms and persist states of the puller. */
@VisibleForTesting protected final PersistAtoms mAtoms;
@@ -324,6 +376,178 @@
saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
}
+ /** Adds a new {@link ImsRegistrationFeatureTagStats} to the storage. */
+ public synchronized void addImsRegistrationFeatureTagStats(
+ ImsRegistrationFeatureTagStats stats) {
+ ImsRegistrationFeatureTagStats existingStats = find(stats);
+ if (existingStats != null) {
+ existingStats.registeredMillis += stats.registeredMillis;
+ } else {
+ mAtoms.imsRegistrationFeatureTagStats =
+ insertAtRandomPlace(mAtoms.imsRegistrationFeatureTagStats,
+ stats, MAX_NUM_IMS_REGISTRATION_FEATURE_STATS);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link RcsClientProvisioningStats} to the storage. */
+ public synchronized void addRcsClientProvisioningStats(RcsClientProvisioningStats stats) {
+ RcsClientProvisioningStats existingStats = find(stats);
+ if (existingStats != null) {
+ existingStats.count += 1;
+ } else {
+ mAtoms.rcsClientProvisioningStats =
+ insertAtRandomPlace(mAtoms.rcsClientProvisioningStats, stats,
+ MAX_NUM_RCS_CLIENT_PROVISIONING_STATS);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link RcsAcsProvisioningStats} to the storage. */
+ public synchronized void addRcsAcsProvisioningStats(RcsAcsProvisioningStats stats) {
+ RcsAcsProvisioningStats existingStats = find(stats);
+ if (existingStats != null) {
+ existingStats.count += 1;
+ existingStats.stateTimerMillis += stats.stateTimerMillis;
+ } else {
+ // prevent that wrong count from caller effects total count
+ stats.count = 1;
+ mAtoms.rcsAcsProvisioningStats =
+ insertAtRandomPlace(mAtoms.rcsAcsProvisioningStats, stats,
+ MAX_NUM_RCS_ACS_PROVISIONING_STATS);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link SipDelegateStats} to the storage. */
+ public synchronized void addSipDelegateStats(SipDelegateStats stats) {
+ mAtoms.sipDelegateStats = insertAtRandomPlace(mAtoms.sipDelegateStats, stats,
+ MAX_NUM_SIP_DELEGATE_STATS);
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link SipTransportFeatureTagStats} to the storage. */
+ public synchronized void addSipTransportFeatureTagStats(SipTransportFeatureTagStats stats) {
+ SipTransportFeatureTagStats lastStat = find(stats);
+ if (lastStat != null) {
+ lastStat.associatedMillis += stats.associatedMillis;
+ } else {
+ mAtoms.sipTransportFeatureTagStats =
+ insertAtRandomPlace(mAtoms.sipTransportFeatureTagStats, stats,
+ MAX_NUM_SIP_TRANSPORT_FEATURE_TAG_STATS);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link SipMessageResponse} to the storage. */
+ public synchronized void addSipMessageResponse(SipMessageResponse stats) {
+ SipMessageResponse existingStats = find(stats);
+ if (existingStats != null) {
+ existingStats.count += 1;
+ } else {
+ mAtoms.sipMessageResponse = insertAtRandomPlace(mAtoms.sipMessageResponse, stats,
+ MAX_NUM_SIP_MESSAGE_RESPONSE_STATS);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link SipTransportSession} to the storage. */
+ public synchronized void addCompleteSipTransportSession(SipTransportSession stats) {
+ SipTransportSession existingStats = find(stats);
+ if (existingStats != null) {
+ existingStats.sessionCount += 1;
+ if (stats.isEndedGracefully) {
+ existingStats.endedGracefullyCount += 1;
+ }
+ } else {
+ mAtoms.sipTransportSession =
+ insertAtRandomPlace(mAtoms.sipTransportSession, stats,
+ MAX_NUM_SIP_TRANSPORT_SESSION_STATS);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link ImsDedicatedBearerListenerEvent} to the storage. */
+ public synchronized void addImsDedicatedBearerListenerEvent(
+ ImsDedicatedBearerListenerEvent stats) {
+ ImsDedicatedBearerListenerEvent existingStats = find(stats);
+ if (existingStats != null) {
+ existingStats.eventCount += 1;
+ } else {
+ mAtoms.imsDedicatedBearerListenerEvent =
+ insertAtRandomPlace(mAtoms.imsDedicatedBearerListenerEvent,
+ stats, MAX_NUM_DEDICATED_BEARER_LISTENER_EVENT_STATS);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link ImsDedicatedBearerEvent} to the storage. */
+ public synchronized void addImsDedicatedBearerEvent(ImsDedicatedBearerEvent stats) {
+ ImsDedicatedBearerEvent existingStats = find(stats);
+ if (existingStats != null) {
+ existingStats.count += 1;
+ } else {
+ mAtoms.imsDedicatedBearerEvent =
+ insertAtRandomPlace(mAtoms.imsDedicatedBearerEvent, stats,
+ MAX_NUM_DEDICATED_BEARER_EVENT_STATS);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link ImsRegistrationServiceDescStats} to the storage. */
+ public synchronized void addImsRegistrationServiceDescStats(
+ ImsRegistrationServiceDescStats stats) {
+ ImsRegistrationServiceDescStats existingStats = find(stats);
+ if (existingStats != null) {
+ existingStats.publishedMillis += stats.publishedMillis;
+ } else {
+ mAtoms.imsRegistrationServiceDescStats =
+ insertAtRandomPlace(mAtoms.imsRegistrationServiceDescStats,
+ stats, MAX_NUM_IMS_REGISTRATION_SERVICE_DESC_STATS);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link UceEventStats} to the storage. */
+ public synchronized void addUceEventStats(UceEventStats stats) {
+ UceEventStats existingStats = find(stats);
+ if (existingStats != null) {
+ existingStats.count += 1;
+ } else {
+ mAtoms.uceEventStats =
+ insertAtRandomPlace(mAtoms.uceEventStats, stats, MAX_NUM_UCE_EVENT_STATS);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link PresenceNotifyEvent} to the storage. */
+ public synchronized void addPresenceNotifyEvent(PresenceNotifyEvent stats) {
+ PresenceNotifyEvent existingStats = find(stats);
+ if (existingStats != null) {
+ existingStats.rcsCapsCount += stats.rcsCapsCount;
+ existingStats.mmtelCapsCount += stats.mmtelCapsCount;
+ existingStats.noCapsCount += stats.noCapsCount;
+ existingStats.count += stats.count;
+ } else {
+ mAtoms.presenceNotifyEvent =
+ insertAtRandomPlace(mAtoms.presenceNotifyEvent, stats,
+ MAX_NUM_PRESENCE_NOTIFY_EVENT_STATS);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
+ /** Adds a new {@link GbaEvent} to the storage. */
+ public synchronized void addGbaEvent(GbaEvent stats) {
+ GbaEvent existingStats = find(stats);
+ if (existingStats != null) {
+ existingStats.count += 1;
+ } else {
+ mAtoms.gbaEvent =
+ insertAtRandomPlace(mAtoms.gbaEvent, stats, MAX_NUM_GBA_EVENT_STATS);
+ }
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
+ }
+
/**
* Returns and clears the voice call sessions if last pulled longer than {@code
* minIntervalMillis} ago, otherwise returns {@code null}.
@@ -507,6 +731,251 @@
}
}
+ /**
+ * Returns and clears the ImsRegistrationFeatureTagStats if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized ImsRegistrationFeatureTagStats[] getImsRegistrationFeatureTagStats(
+ long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.imsRegistrationFeatureTagStatsPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.imsRegistrationFeatureTagStatsPullTimestampMillis = getWallTimeMillis();
+ ImsRegistrationFeatureTagStats[] previousStats =
+ mAtoms.imsRegistrationFeatureTagStats;
+ mAtoms.imsRegistrationFeatureTagStats = new ImsRegistrationFeatureTagStats[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousStats;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the RcsClientProvisioningStats if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized RcsClientProvisioningStats[] getRcsClientProvisioningStats(
+ long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.rcsClientProvisioningStatsPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.rcsClientProvisioningStatsPullTimestampMillis = getWallTimeMillis();
+ RcsClientProvisioningStats[] previousStats = mAtoms.rcsClientProvisioningStats;
+ mAtoms.rcsClientProvisioningStats = new RcsClientProvisioningStats[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousStats;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the RcsAcsProvisioningStats if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized RcsAcsProvisioningStats[] getRcsAcsProvisioningStats(
+ long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.rcsAcsProvisioningStatsPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.rcsAcsProvisioningStatsPullTimestampMillis = getWallTimeMillis();
+ RcsAcsProvisioningStats[] previousStats = mAtoms.rcsAcsProvisioningStats;
+ mAtoms.rcsAcsProvisioningStats = new RcsAcsProvisioningStats[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousStats;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the SipDelegateStats if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized SipDelegateStats[] getSipDelegateStats(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.sipDelegateStatsPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.sipDelegateStatsPullTimestampMillis = getWallTimeMillis();
+ SipDelegateStats[] previousStats = mAtoms.sipDelegateStats;
+ mAtoms.sipDelegateStats = new SipDelegateStats[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousStats;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the SipTransportFeatureTagStats if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized SipTransportFeatureTagStats[] getSipTransportFeatureTagStats(
+ long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.sipTransportFeatureTagStatsPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.sipTransportFeatureTagStatsPullTimestampMillis = getWallTimeMillis();
+ SipTransportFeatureTagStats[] previousStats = mAtoms.sipTransportFeatureTagStats;
+ mAtoms.sipTransportFeatureTagStats = new SipTransportFeatureTagStats[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousStats;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the SipMessageResponse if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized SipMessageResponse[] getSipMessageResponse(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.sipMessageResponsePullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.sipMessageResponsePullTimestampMillis = getWallTimeMillis();
+ SipMessageResponse[] previousStats =
+ mAtoms.sipMessageResponse;
+ mAtoms.sipMessageResponse = new SipMessageResponse[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousStats;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the SipTransportSession if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized SipTransportSession[] getSipTransportSession(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.sipTransportSessionPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.sipTransportSessionPullTimestampMillis = getWallTimeMillis();
+ SipTransportSession[] previousStats =
+ mAtoms.sipTransportSession;
+ mAtoms.sipTransportSession = new SipTransportSession[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousStats;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the ImsDedicatedBearerListenerEvent if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized ImsDedicatedBearerListenerEvent[] getImsDedicatedBearerListenerEvent(
+ long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.imsDedicatedBearerListenerEventPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.imsDedicatedBearerListenerEventPullTimestampMillis = getWallTimeMillis();
+ ImsDedicatedBearerListenerEvent[] previousStats =
+ mAtoms.imsDedicatedBearerListenerEvent;
+ mAtoms.imsDedicatedBearerListenerEvent = new ImsDedicatedBearerListenerEvent[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousStats;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the ImsDedicatedBearerEvent if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized ImsDedicatedBearerEvent[] getImsDedicatedBearerEvent(
+ long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.imsDedicatedBearerEventPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.imsDedicatedBearerEventPullTimestampMillis = getWallTimeMillis();
+ ImsDedicatedBearerEvent[] previousStats =
+ mAtoms.imsDedicatedBearerEvent;
+ mAtoms.imsDedicatedBearerEvent = new ImsDedicatedBearerEvent[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousStats;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the ImsRegistrationServiceDescStats if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized ImsRegistrationServiceDescStats[] getImsRegistrationServiceDescStats(long
+ minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.imsRegistrationServiceDescStatsPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.imsRegistrationServiceDescStatsPullTimestampMillis = getWallTimeMillis();
+ ImsRegistrationServiceDescStats[] previousStats =
+ mAtoms.imsRegistrationServiceDescStats;
+ mAtoms.imsRegistrationServiceDescStats = new ImsRegistrationServiceDescStats[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousStats;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the UceEventStats if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized UceEventStats[] getUceEventStats(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.uceEventStatsPullTimestampMillis > minIntervalMillis) {
+ mAtoms.uceEventStatsPullTimestampMillis = getWallTimeMillis();
+ UceEventStats[] previousStats = mAtoms.uceEventStats;
+ mAtoms.uceEventStats = new UceEventStats[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousStats;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the PresenceNotifyEvent if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized PresenceNotifyEvent[] getPresenceNotifyEvent(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.presenceNotifyEventPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.presenceNotifyEventPullTimestampMillis = getWallTimeMillis();
+ PresenceNotifyEvent[] previousStats = mAtoms.presenceNotifyEvent;
+ mAtoms.presenceNotifyEvent = new PresenceNotifyEvent[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousStats;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the GbaEvent if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized GbaEvent[] getGbaEvent(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.gbaEventPullTimestampMillis > minIntervalMillis) {
+ mAtoms.gbaEventPullTimestampMillis = getWallTimeMillis();
+ GbaEvent[] previousStats = mAtoms.gbaEvent;
+ mAtoms.gbaEvent = new GbaEvent[0];
+ saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_GET_MILLIS);
+ return previousStats;
+ } else {
+ return null;
+ }
+ }
+
/** Loads {@link PersistAtoms} from a file in private storage. */
private PersistAtoms loadAtomsFromFile() {
try {
@@ -558,6 +1027,72 @@
ImsRegistrationTermination.class,
MAX_NUM_IMS_REGISTRATION_TERMINATIONS);
atoms.networkRequests = sanitizeAtoms(atoms.networkRequests, NetworkRequests.class);
+ atoms.imsRegistrationFeatureTagStats =
+ sanitizeAtoms(
+ atoms.imsRegistrationFeatureTagStats,
+ ImsRegistrationFeatureTagStats.class,
+ MAX_NUM_IMS_REGISTRATION_FEATURE_STATS);
+ atoms.rcsClientProvisioningStats =
+ sanitizeAtoms(
+ atoms.rcsClientProvisioningStats,
+ RcsClientProvisioningStats.class,
+ MAX_NUM_RCS_CLIENT_PROVISIONING_STATS);
+ atoms.rcsAcsProvisioningStats =
+ sanitizeAtoms(
+ atoms.rcsAcsProvisioningStats,
+ RcsAcsProvisioningStats.class,
+ MAX_NUM_RCS_ACS_PROVISIONING_STATS);
+ atoms.sipDelegateStats =
+ sanitizeAtoms(
+ atoms.sipDelegateStats,
+ SipDelegateStats.class,
+ MAX_NUM_SIP_DELEGATE_STATS);
+ atoms.sipTransportFeatureTagStats =
+ sanitizeAtoms(
+ atoms.sipTransportFeatureTagStats,
+ SipTransportFeatureTagStats.class,
+ MAX_NUM_SIP_TRANSPORT_FEATURE_TAG_STATS);
+ atoms.sipMessageResponse =
+ sanitizeAtoms(
+ atoms.sipMessageResponse,
+ SipMessageResponse.class,
+ MAX_NUM_SIP_MESSAGE_RESPONSE_STATS);
+ atoms.sipTransportSession =
+ sanitizeAtoms(
+ atoms.sipTransportSession,
+ SipTransportSession.class,
+ MAX_NUM_SIP_TRANSPORT_SESSION_STATS);
+ atoms.imsDedicatedBearerListenerEvent =
+ sanitizeAtoms(
+ atoms.imsDedicatedBearerListenerEvent,
+ ImsDedicatedBearerListenerEvent.class,
+ MAX_NUM_DEDICATED_BEARER_LISTENER_EVENT_STATS);
+ atoms.imsDedicatedBearerEvent =
+ sanitizeAtoms(
+ atoms.imsDedicatedBearerEvent,
+ ImsDedicatedBearerEvent.class,
+ MAX_NUM_DEDICATED_BEARER_EVENT_STATS);
+ atoms.imsRegistrationServiceDescStats =
+ sanitizeAtoms(
+ atoms.imsRegistrationServiceDescStats,
+ ImsRegistrationServiceDescStats.class,
+ MAX_NUM_IMS_REGISTRATION_SERVICE_DESC_STATS);
+ atoms.uceEventStats =
+ sanitizeAtoms(
+ atoms.uceEventStats,
+ UceEventStats.class,
+ MAX_NUM_UCE_EVENT_STATS);
+ atoms.presenceNotifyEvent =
+ sanitizeAtoms(
+ atoms.presenceNotifyEvent,
+ PresenceNotifyEvent.class,
+ MAX_NUM_PRESENCE_NOTIFY_EVENT_STATS);
+ atoms.gbaEvent =
+ sanitizeAtoms(
+ atoms.gbaEvent,
+ GbaEvent.class,
+ MAX_NUM_GBA_EVENT_STATS);
+
// out of caution, sanitize also the timestamps
atoms.voiceCallRatUsagePullTimestampMillis =
sanitizeTimestamp(atoms.voiceCallRatUsagePullTimestampMillis);
@@ -579,6 +1114,33 @@
sanitizeTimestamp(atoms.imsRegistrationTerminationPullTimestampMillis);
atoms.networkRequestsPullTimestampMillis =
sanitizeTimestamp(atoms.networkRequestsPullTimestampMillis);
+ atoms.imsRegistrationFeatureTagStatsPullTimestampMillis =
+ sanitizeTimestamp(atoms.imsRegistrationFeatureTagStatsPullTimestampMillis);
+ atoms.rcsClientProvisioningStatsPullTimestampMillis =
+ sanitizeTimestamp(atoms.rcsClientProvisioningStatsPullTimestampMillis);
+ atoms.rcsAcsProvisioningStatsPullTimestampMillis =
+ sanitizeTimestamp(atoms.rcsAcsProvisioningStatsPullTimestampMillis);
+ atoms.sipDelegateStatsPullTimestampMillis =
+ sanitizeTimestamp(atoms.sipDelegateStatsPullTimestampMillis);
+ atoms.sipTransportFeatureTagStatsPullTimestampMillis =
+ sanitizeTimestamp(atoms.sipTransportFeatureTagStatsPullTimestampMillis);
+ atoms.sipMessageResponsePullTimestampMillis =
+ sanitizeTimestamp(atoms.sipMessageResponsePullTimestampMillis);
+ atoms.sipTransportSessionPullTimestampMillis =
+ sanitizeTimestamp(atoms.sipTransportSessionPullTimestampMillis);
+ atoms.imsDedicatedBearerListenerEventPullTimestampMillis =
+ sanitizeTimestamp(atoms.imsDedicatedBearerListenerEventPullTimestampMillis);
+ atoms.imsDedicatedBearerEventPullTimestampMillis =
+ sanitizeTimestamp(atoms.imsDedicatedBearerEventPullTimestampMillis);
+ atoms.imsRegistrationServiceDescStatsPullTimestampMillis =
+ sanitizeTimestamp(atoms.imsRegistrationServiceDescStatsPullTimestampMillis);
+ atoms.uceEventStatsPullTimestampMillis =
+ sanitizeTimestamp(atoms.uceEventStatsPullTimestampMillis);
+ atoms.presenceNotifyEventPullTimestampMillis =
+ sanitizeTimestamp(atoms.presenceNotifyEventPullTimestampMillis);
+ atoms.gbaEventPullTimestampMillis =
+ sanitizeTimestamp(atoms.gbaEventPullTimestampMillis);
+
return atoms;
} catch (NoSuchFileException e) {
Rlog.d(TAG, "PersistAtoms file not found");
@@ -726,6 +1288,213 @@
}
return -1;
}
+ /**
+ * Returns the Dedicated Bearer Listener event that has the same carrier id, slot id, rat, qci
+ * and established state as the given one, or {@code null} if it does not exist.
+ */
+ private @Nullable ImsDedicatedBearerListenerEvent find(ImsDedicatedBearerListenerEvent key) {
+ for (ImsDedicatedBearerListenerEvent stats : mAtoms.imsDedicatedBearerListenerEvent) {
+ if (stats.carrierId == key.carrierId
+ && stats.slotId == key.slotId
+ && stats.ratAtEnd == key.ratAtEnd
+ && stats.qci == key.qci
+ && stats.dedicatedBearerEstablished == key.dedicatedBearerEstablished) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the Dedicated Bearer event that has the same carrier id, slot id, rat,
+ * qci, bearer state, local/remote connection and exsting listener as the given one,
+ * or {@code null} if it does not exist.
+ */
+ private @Nullable ImsDedicatedBearerEvent find(ImsDedicatedBearerEvent key) {
+ for (ImsDedicatedBearerEvent stats : mAtoms.imsDedicatedBearerEvent) {
+ if (stats.carrierId == key.carrierId
+ && stats.slotId == key.slotId
+ && stats.ratAtEnd == key.ratAtEnd
+ && stats.qci == key.qci
+ && stats.bearerState == key.bearerState
+ && stats.localConnectionInfoReceived == key.localConnectionInfoReceived
+ && stats.remoteConnectionInfoReceived == key.remoteConnectionInfoReceived
+ && stats.hasListeners == key.hasListeners) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the Registration Feature Tag that has the same carrier id, slot id,
+ * feature tag name or custom feature tag name and registration tech as the given one,
+ * or {@code null} if it does not exist.
+ */
+ private @Nullable ImsRegistrationFeatureTagStats find(ImsRegistrationFeatureTagStats key) {
+ for (ImsRegistrationFeatureTagStats stats : mAtoms.imsRegistrationFeatureTagStats) {
+ if (stats.carrierId == key.carrierId
+ && stats.slotId == key.slotId
+ && stats.featureTagName == key.featureTagName
+ && stats.registrationTech == key.registrationTech) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns Client Provisioning that has the same carrier id, slot id and event as the given
+ * one, or {@code null} if it does not exist.
+ */
+ private @Nullable RcsClientProvisioningStats find(RcsClientProvisioningStats key) {
+ for (RcsClientProvisioningStats stats : mAtoms.rcsClientProvisioningStats) {
+ if (stats.carrierId == key.carrierId
+ && stats.slotId == key.slotId
+ && stats.event == key.event) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns ACS Provisioning that has the same carrier id, slot id, response code, response type
+ * and SR supported as the given one, or {@code null} if it does not exist.
+ */
+ private @Nullable RcsAcsProvisioningStats find(RcsAcsProvisioningStats key) {
+ for (RcsAcsProvisioningStats stats : mAtoms.rcsAcsProvisioningStats) {
+ if (stats.carrierId == key.carrierId
+ && stats.slotId == key.slotId
+ && stats.responseCode == key.responseCode
+ && stats.responseType == key.responseType
+ && stats.isSingleRegistrationEnabled == key.isSingleRegistrationEnabled) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns Sip Message Response that has the same carrier id, slot id, method, response,
+ * direction and error as the given one, or {@code null} if it does not exist.
+ */
+ private @Nullable SipMessageResponse find(SipMessageResponse key) {
+ for (SipMessageResponse stats : mAtoms.sipMessageResponse) {
+ if (stats.carrierId == key.carrierId
+ && stats.slotId == key.slotId
+ && stats.sipMessageMethod == key.sipMessageMethod
+ && stats.sipMessageResponse == key.sipMessageResponse
+ && stats.sipMessageDirection == key.sipMessageDirection
+ && stats.messageError == key.messageError) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns Sip Transport Session that has the same carrier id, slot id, method, direction and
+ * response as the given one, or {@code null} if it does not exist.
+ */
+ private @Nullable SipTransportSession find(SipTransportSession key) {
+ for (SipTransportSession stats : mAtoms.sipTransportSession) {
+ if (stats.carrierId == key.carrierId
+ && stats.slotId == key.slotId
+ && stats.sessionMethod == key.sessionMethod
+ && stats.sipMessageDirection == key.sipMessageDirection
+ && stats.sipResponse == key.sipResponse) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns Registration Service Desc Stats that has the same carrier id, slot id, service id or
+ * custom service id, service id version and registration tech as the given one,
+ * or {@code null} if it does not exist.
+ */
+ private @Nullable ImsRegistrationServiceDescStats find(ImsRegistrationServiceDescStats key) {
+ for (ImsRegistrationServiceDescStats stats : mAtoms.imsRegistrationServiceDescStats) {
+ if (stats.carrierId == key.carrierId
+ && stats.slotId == key.slotId
+ && stats.serviceIdName == key.serviceIdName
+ && stats.serviceIdVersion == key.serviceIdVersion
+ && stats.registrationTech == key.registrationTech) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns UCE Event Stats that has the same carrier id, slot id, event result, command code and
+ * network response as the given one, or {@code null} if it does not exist.
+ */
+ private @Nullable UceEventStats find(UceEventStats key) {
+ for (UceEventStats stats : mAtoms.uceEventStats) {
+ if (stats.carrierId == key.carrierId
+ && stats.slotId == key.slotId
+ && stats.type == key.type
+ && stats.successful == key.successful
+ && stats.commandCode == key.commandCode
+ && stats.networkResponse == key.networkResponse) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns Presence Notify Event that has the same carrier id, slot id, reason and body in
+ * response as the given one, or {@code null} if it does not exist.
+ */
+ private @Nullable PresenceNotifyEvent find(PresenceNotifyEvent key) {
+ for (PresenceNotifyEvent stats : mAtoms.presenceNotifyEvent) {
+ if (stats.carrierId == key.carrierId
+ && stats.slotId == key.slotId
+ && stats.reason == key.reason
+ && stats.contentBodyReceived == key.contentBodyReceived) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns GBA Event that has the same carrier id, slot id, result of operation and fail reason
+ * as the given one, or {@code null} if it does not exist.
+ */
+ private @Nullable GbaEvent find(GbaEvent key) {
+ for (GbaEvent stats : mAtoms.gbaEvent) {
+ if (stats.carrierId == key.carrierId
+ && stats.slotId == key.slotId
+ && stats.successful == key.successful
+ && stats.failedReason == key.failedReason) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns Sip Transport Feature Tag Stats that has the same carrier id, slot id, feature tag
+ * name, deregister reason, denied reason and feature tag name or custom feature tag name as
+ * the given one, or {@code null} if it does not exist.
+ */
+ private @Nullable SipTransportFeatureTagStats find(SipTransportFeatureTagStats key) {
+ for (SipTransportFeatureTagStats stat: mAtoms.sipTransportFeatureTagStats) {
+ if (stat.carrierId == key.carrierId
+ && stat.slotId == key.slotId
+ && stat.featureTagName == key.featureTagName
+ && stat.sipTransportDeregisteredReason == key.sipTransportDeregisteredReason
+ && stat.sipTransportDeniedReason == key.sipTransportDeniedReason) {
+ return stat;
+ }
+ }
+ return null;
+ }
/**
* Inserts a new element in a random position in an array with a maximum size, replacing the
@@ -817,6 +1586,20 @@
atoms.imsRegistrationStatsPullTimestampMillis = currentTime;
atoms.imsRegistrationTerminationPullTimestampMillis = currentTime;
atoms.networkRequestsPullTimestampMillis = currentTime;
+ atoms.imsRegistrationFeatureTagStatsPullTimestampMillis = currentTime;
+ atoms.rcsClientProvisioningStatsPullTimestampMillis = currentTime;
+ atoms.rcsAcsProvisioningStatsPullTimestampMillis = currentTime;
+ atoms.sipDelegateStatsPullTimestampMillis = currentTime;
+ atoms.sipTransportFeatureTagStatsPullTimestampMillis = currentTime;
+ atoms.sipMessageResponsePullTimestampMillis = currentTime;
+ atoms.sipTransportSessionPullTimestampMillis = currentTime;
+ atoms.imsDedicatedBearerListenerEventPullTimestampMillis = currentTime;
+ atoms.imsDedicatedBearerEventPullTimestampMillis = currentTime;
+ atoms.imsRegistrationServiceDescStatsPullTimestampMillis = currentTime;
+ atoms.uceEventStatsPullTimestampMillis = currentTime;
+ atoms.presenceNotifyEventPullTimestampMillis = currentTime;
+ atoms.gbaEventPullTimestampMillis = currentTime;
+
Rlog.d(TAG, "created new PersistAtoms");
return atoms;
}
diff --git a/src/java/com/android/internal/telephony/metrics/RcsStats.java b/src/java/com/android/internal/telephony/metrics/RcsStats.java
new file mode 100644
index 0000000..de77b12
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/RcsStats.java
@@ -0,0 +1,1860 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CALL_COMPOSER;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT_ROLE;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT_STANDALONE;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHAT_V1;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHAT_V2;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CUSTOM;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_FT;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_FT_OVER_SMS;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_GEO_PUSH;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_GEO_PUSH_VIA_SMS;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_MMTEL;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_POST_CALL;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_SHARED_MAP;
+import static com.android.internal.telephony.TelephonyStatsLog.IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_SHARED_SKETCH;
+import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT__REASON__REASON_CUSTOM;
+import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT__REASON__REASON_DEACTIVATED;
+import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT__REASON__REASON_GIVEUP;
+import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT__REASON__REASON_NORESOURCE;
+import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT__REASON__REASON_PROBATION;
+import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT__REASON__REASON_REJECTED;
+import static com.android.internal.telephony.TelephonyStatsLog.PRESENCE_NOTIFY_EVENT__REASON__REASON_TIMEOUT;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__ERROR;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PRE_PROVISIONING_XML;
+import static com.android.internal.telephony.TelephonyStatsLog.UCE_EVENT_STATS__TYPE__INCOMING_OPTION;
+import static com.android.internal.telephony.TelephonyStatsLog.UCE_EVENT_STATS__TYPE__OUTGOING_OPTION;
+import static com.android.internal.telephony.TelephonyStatsLog.UCE_EVENT_STATS__TYPE__PUBLISH;
+import static com.android.internal.telephony.TelephonyStatsLog.UCE_EVENT_STATS__TYPE__SUBSCRIBE;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.TelephonyProtoEnums;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.RcsContactPresenceTuple;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.aidl.IRcsConfigCallback;
+import android.util.Base64;
+import android.util.IndentingPrintWriter;
+
+import com.android.ims.rcs.uce.UceStatsWriter;
+import com.android.ims.rcs.uce.UceStatsWriter.UceStatsCallback;
+import com.android.ims.rcs.uce.util.FeatureTags;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.nano.PersistAtomsProto;
+import com.android.internal.telephony.nano.PersistAtomsProto.GbaEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerListenerEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationFeatureTagStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationServiceDescStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.RcsClientProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipDelegateStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipMessageResponse;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportFeatureTagStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.UceEventStats;
+import com.android.telephony.Rlog;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+/** Tracks RCS provisioning, sip transport, UCE metrics for phone. */
+public class RcsStats {
+ private static final String TAG = RcsStats.class.getSimpleName();
+ private static final long MIN_DURATION_MILLIS = 1L * SECOND_IN_MILLIS;
+ private final PersistAtomsStorage mAtomsStorage =
+ PhoneFactory.getMetricsCollector().getAtomsStorage();
+ private static final Random RANDOM = new Random();
+
+ private UceStatsWriterCallback mCallback;
+ private static RcsStats sInstance;
+
+ public static final int NONE = -1;
+ public static final int STATE_REGISTERED = 0;
+ public static final int STATE_DEREGISTERED = 1;
+ public static final int STATE_DENIED = 2;
+
+ private static final String SIP_REQUEST_MESSAGE_TYPE_INVITE = "INVITE";
+ private static final String SIP_REQUEST_MESSAGE_TYPE_ACK = "ACK";
+ private static final String SIP_REQUEST_MESSAGE_TYPE_OPTIONS = "OPTIONS";
+ private static final String SIP_REQUEST_MESSAGE_TYPE_BYE = "BYE";
+ private static final String SIP_REQUEST_MESSAGE_TYPE_CANCEL = "CANCEL";
+ private static final String SIP_REQUEST_MESSAGE_TYPE_REGISTER = "REGISTER";
+ private static final String SIP_REQUEST_MESSAGE_TYPE_PRACK = "PRACK";
+ private static final String SIP_REQUEST_MESSAGE_TYPE_SUBSCRIBE = "SUBSCRIBE";
+ private static final String SIP_REQUEST_MESSAGE_TYPE_NOTIFY = "NOTIFY";
+ private static final String SIP_REQUEST_MESSAGE_TYPE_PUBLISH = "PUBLISH";
+ private static final String SIP_REQUEST_MESSAGE_TYPE_INFO = "INFO";
+ private static final String SIP_REQUEST_MESSAGE_TYPE_REFER = "REFER";
+ private static final String SIP_REQUEST_MESSAGE_TYPE_MESSAGE = "MESSAGE";
+ private static final String SIP_REQUEST_MESSAGE_TYPE_UPDATE = "UPDATE";
+
+ /**
+ * Describe Feature Tags
+ * See frameworks/opt/net/ims/src/java/com/android/ims/rcs/uce/util/FeatureTags.java
+ * and int value matching the Feature Tags
+ * See stats/enums/telephony/enums.proto
+ */
+ private static final Map<String, Integer> FEATURE_TAGS = new HashMap<>();
+
+ static {
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_STANDALONE_MSG.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_STANDALONE_MSG);
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHAT_IM.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_CHAT_IM);
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHAT_SESSION.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_CHAT_SESSION);
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_FILE_TRANSFER.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_FILE_TRANSFER);
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_FILE_TRANSFER_VIA_SMS.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_FILE_TRANSFER_VIA_SMS);
+ FEATURE_TAGS.put(
+ FeatureTags.FEATURE_TAG_CALL_COMPOSER_ENRICHED_CALLING.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_CALL_COMPOSER_ENRICHED_CALLING);
+ FEATURE_TAGS.put(
+ FeatureTags.FEATURE_TAG_CALL_COMPOSER_VIA_TELEPHONY.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_CALL_COMPOSER_VIA_TELEPHONY);
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_POST_CALL.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_POST_CALL);
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_SHARED_MAP.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_SHARED_MAP);
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_SHARED_SKETCH.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_SHARED_SKETCH);
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_GEO_PUSH.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_GEO_PUSH);
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_GEO_PUSH_VIA_SMS.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_GEO_PUSH_VIA_SMS);
+ FEATURE_TAGS.put(
+ FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION);
+ String FeatureTag = FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_STANDALONE_MSG;
+ FEATURE_TAGS.put(FeatureTag.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_COMMUNICATION_USING_STANDALONE_MSG);
+ FEATURE_TAGS.put(
+ FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_VERSION_SUPPORTED);
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_CHATBOT_ROLE.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_CHATBOT_ROLE);
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_MMTEL.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_MMTEL);
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_VIDEO.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_VIDEO);
+ FEATURE_TAGS.put(FeatureTags.FEATURE_TAG_PRESENCE.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_PRESENCE);
+ }
+
+ /**
+ * Describe Service IDs
+ * See frameworks/base/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+ * and int value matching the service IDs
+ * See frameworks/proto_logging/stats/atoms.proto
+ */
+ private static final Map<String, Integer> SERVICE_IDS = new HashMap<>();
+
+ static {
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_MMTEL.trim().toLowerCase(),
+ IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_MMTEL);
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHAT_V1.trim().toLowerCase(),
+ IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHAT_V1);
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHAT_V2.trim().toLowerCase(),
+ IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHAT_V2);
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_FT.trim().toLowerCase(),
+ IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_FT);
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_FT_OVER_SMS.trim().toLowerCase(),
+ IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_FT_OVER_SMS);
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH.trim().toLowerCase(),
+ IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_GEO_PUSH);
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH_VIA_SMS.trim().toLowerCase(),
+ IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_GEO_PUSH_VIA_SMS);
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CALL_COMPOSER.trim().toLowerCase(),
+ IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CALL_COMPOSER);
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_POST_CALL.trim().toLowerCase(),
+ IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_POST_CALL);
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_SHARED_MAP.trim().toLowerCase(),
+ IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_SHARED_MAP);
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_SHARED_SKETCH.trim().toLowerCase(),
+ IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_SHARED_SKETCH);
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT.trim().toLowerCase(),
+ IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT);
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_STANDALONE.trim().toLowerCase(),
+ IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT_STANDALONE
+ );
+ SERVICE_IDS.put(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_ROLE.trim().toLowerCase(),
+ IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CHATBOT_ROLE);
+ }
+
+ /**
+ * Describe Message Method Type
+ * See stats/enums/telephony/enums.proto
+ */
+ private static final Map<String, Integer> MESSAGE_TYPE = new HashMap<>();
+
+ static {
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_INVITE.trim().toLowerCase(),
+ TelephonyProtoEnums.SIP_REQUEST_INVITE);
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_ACK.trim().toLowerCase(),
+ TelephonyProtoEnums.SIP_REQUEST_ACK);
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_OPTIONS.trim().toLowerCase(),
+ TelephonyProtoEnums.SIP_REQUEST_OPTIONS);
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_BYE.trim().toLowerCase(),
+ TelephonyProtoEnums.SIP_REQUEST_BYE);
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_CANCEL.trim().toLowerCase(),
+ TelephonyProtoEnums.SIP_REQUEST_CANCEL);
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_REGISTER.trim().toLowerCase(),
+ TelephonyProtoEnums.SIP_REQUEST_REGISTER);
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_PRACK.trim().toLowerCase(),
+ TelephonyProtoEnums.SIP_REQUEST_PRACK);
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_SUBSCRIBE.trim().toLowerCase(),
+ TelephonyProtoEnums.SIP_REQUEST_SUBSCRIBE);
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_NOTIFY.trim().toLowerCase(),
+ TelephonyProtoEnums.SIP_REQUEST_NOTIFY);
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_PUBLISH.trim().toLowerCase(),
+ TelephonyProtoEnums.SIP_REQUEST_PUBLISH);
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_INFO.trim().toLowerCase(),
+ TelephonyProtoEnums.SIP_REQUEST_INFO);
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_REFER.trim().toLowerCase(),
+ TelephonyProtoEnums.SIP_REQUEST_REFER);
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_MESSAGE.trim().toLowerCase(),
+ TelephonyProtoEnums.SIP_REQUEST_MESSAGE);
+ MESSAGE_TYPE.put(SIP_REQUEST_MESSAGE_TYPE_UPDATE.trim().toLowerCase(),
+ TelephonyProtoEnums.SIP_REQUEST_UPDATE);
+ }
+
+ /**
+ * Describe Reasons
+ * See frameworks/opt/net/ims/src/java/com/android/ims/rcs/uce/request/
+ * SubscriptionTerminatedHelper.java
+ * and int value matching the Reasons
+ * See frameworks/proto_logging/stats/atoms.proto
+ */
+ private static final Map<String, Integer> NOTIFY_REASONS = new HashMap<>();
+
+ static {
+ NOTIFY_REASONS.put("deactivated", PRESENCE_NOTIFY_EVENT__REASON__REASON_DEACTIVATED);
+ NOTIFY_REASONS.put("probation", PRESENCE_NOTIFY_EVENT__REASON__REASON_PROBATION);
+ NOTIFY_REASONS.put("rejected", PRESENCE_NOTIFY_EVENT__REASON__REASON_REJECTED);
+ NOTIFY_REASONS.put("timeout", PRESENCE_NOTIFY_EVENT__REASON__REASON_TIMEOUT);
+ NOTIFY_REASONS.put("giveup", PRESENCE_NOTIFY_EVENT__REASON__REASON_GIVEUP);
+ NOTIFY_REASONS.put("noresource", PRESENCE_NOTIFY_EVENT__REASON__REASON_NORESOURCE);
+ }
+
+ /**
+ * Describe Rcs Capability set
+ * See frameworks/base/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+ */
+ private static final HashSet<String> RCS_SERVICE_ID_SET = new HashSet<>();
+ static {
+ RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_CHAT_V1);
+ RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_CHAT_V2);
+ RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_FT);
+ RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_FT_OVER_SMS);
+ RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH);
+ RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_GEO_PUSH_VIA_SMS);
+ RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_SHARED_MAP);
+ RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_SHARED_SKETCH);
+ RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_CHATBOT);
+ RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_STANDALONE);
+ RCS_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_CHATBOT_ROLE);
+ }
+
+ /**
+ * Describe Mmtel Capability set
+ * See frameworks/base/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+ */
+ private static final HashSet<String> MMTEL_SERVICE_ID_SET = new HashSet<>();
+ static {
+ MMTEL_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_MMTEL);
+ MMTEL_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_CALL_COMPOSER);
+ MMTEL_SERVICE_ID_SET.add(RcsContactPresenceTuple.SERVICE_ID_POST_CALL);
+ }
+
+ private static final Map<Long, Integer> sSubscribeTaskIds = new HashMap<>();
+ private static final int SUBSCRIBE_SUCCESS = 1;
+ private static final int SUBSCRIBE_NOTIFY = 2;
+
+ @VisibleForTesting
+ protected final Map<Integer, ImsDedicatedBearerListenerEvent> mDedicatedBearerListenerEventMap =
+ new HashMap<>();
+ @VisibleForTesting
+ protected final List<RcsAcsProvisioningStats> mRcsAcsProvisioningStatsList =
+ new ArrayList<RcsAcsProvisioningStats>();
+ @VisibleForTesting
+ protected final HashMap<Integer, RcsProvisioningCallback> mRcsProvisioningCallbackMap =
+ new HashMap<>();
+
+ // Maps feature tag name -> ImsRegistrationFeatureTagStats.
+ private final List<ImsRegistrationFeatureTagStats> mImsRegistrationFeatureTagStatsList =
+ new ArrayList<>();
+
+ // Maps service id -> ImsRegistrationServiceDescStats.
+ @VisibleForTesting
+ protected final List<ImsRegistrationServiceDescStats> mImsRegistrationServiceDescStatsList =
+ new ArrayList<>();
+
+ private List<LastSipDelegateStat> mLastSipDelegateStatList = new ArrayList<>();
+ private HashMap<Integer, SipTransportFeatureTags> mLastFeatureTagStatMap = new HashMap<>();
+ private ArrayList<SipMessageArray> mSipMessageArray = new ArrayList<>();
+ private ArrayList<SipTransportSessionArray> mSipTransportSessionArray = new ArrayList<>();
+ private SipTransportSessionArray mSipTransportSession;
+ private SipMessageArray mSipMessage;
+
+ private class LastSipDelegateStat {
+ public int mSubId;
+ public SipDelegateStats mLastStat;
+ private Set<String> mSupportedTags;
+
+ LastSipDelegateStat(int subId, Set<String> supportedTags) {
+ mSubId = subId;
+ mSupportedTags = supportedTags;
+ }
+
+ public void createSipDelegateStat(int subId) {
+ mLastStat = getDefaultSipDelegateStat(subId);
+ mLastStat.uptimeMillis = getWallTimeMillis();
+ mLastStat.destroyReason = NONE;
+ }
+
+ public void setSipDelegateDestroyReason(int destroyReason) {
+ mLastStat.destroyReason = destroyReason;
+ }
+
+ public boolean isDestroyed() {
+ return mLastStat.destroyReason > NONE;
+ }
+
+ public void conclude(long now) {
+ long duration = now - mLastStat.uptimeMillis;
+ if (duration < MIN_DURATION_MILLIS) {
+ logd("concludeSipDelegateStat: discarding transient stats,"
+ + " duration= " + duration);
+ } else {
+ mLastStat.uptimeMillis = duration;
+ mAtomsStorage.addSipDelegateStats(copyOf(mLastStat));
+ }
+ mLastStat.uptimeMillis = now;
+ }
+
+ public boolean compare(int subId, Set<String> supportedTags) {
+ if (subId != mSubId || supportedTags == null || supportedTags.isEmpty()) {
+ return false;
+ }
+ for (String tag : supportedTags) {
+ if (!mSupportedTags.contains(tag)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private SipDelegateStats getDefaultSipDelegateStat(int subId) {
+ SipDelegateStats stat = new SipDelegateStats();
+ stat.dimension = RANDOM.nextInt();
+ stat.carrierId = getCarrierId(subId);
+ stat.slotId = getSlotId(subId);
+ return stat;
+ }
+ }
+
+ private static SipDelegateStats copyOf(@NonNull SipDelegateStats source) {
+ SipDelegateStats newStat = new SipDelegateStats();
+
+ newStat.dimension = source.dimension;
+ newStat.slotId = source.slotId;
+ newStat.carrierId = source.carrierId;
+ newStat.destroyReason = source.destroyReason;
+ newStat.uptimeMillis = source.uptimeMillis;
+
+ return newStat;
+ }
+
+ private class SipTransportFeatureTags {
+ private HashMap<String, LastFeatureTagState> mFeatureTagMap;
+ private int mSubId;
+
+ private class LastFeatureTagState {
+ public long timeStamp;
+ public int carrierId;
+ public int slotId;
+ public int state;
+ public int reason;
+
+ LastFeatureTagState(int carrierId, int slotId, int state, int reason, long timeStamp) {
+ this.carrierId = carrierId;
+ this.slotId = slotId;
+ this.state = state;
+ this.reason = reason;
+ this.timeStamp = timeStamp;
+ }
+
+ public void update(int state, int reason, long timeStamp) {
+ this.state = state;
+ this.reason = reason;
+ this.timeStamp = timeStamp;
+ }
+
+ public void update(long timeStamp) {
+ this.timeStamp = timeStamp;
+ }
+ }
+
+ SipTransportFeatureTags(int subId) {
+ mFeatureTagMap = new HashMap<>();
+ mSubId = subId;
+ }
+
+ public HashMap<String, LastFeatureTagState> getLastTagStates() {
+ return mFeatureTagMap;
+ }
+
+ /*** Create or update featureTags whenever feature Tag states are changed */
+ public synchronized void updateLastFeatureTagState(String tagName, int state, int reason,
+ long timeStamp) {
+ int carrierId = getCarrierId(mSubId);
+ int slotId = getSlotId(mSubId);
+ if (mFeatureTagMap.containsKey(tagName)) {
+ LastFeatureTagState lastFeatureTagState = mFeatureTagMap.get(tagName);
+ if (lastFeatureTagState != null) {
+ addFeatureTagStat(tagName, lastFeatureTagState, timeStamp);
+ lastFeatureTagState.update(state, reason, timeStamp);
+ } else {
+ create(tagName, carrierId, slotId, state, reason, timeStamp);
+ }
+
+ } else {
+ create(tagName, carrierId, slotId, state, reason, timeStamp);
+ }
+ }
+
+ /** Update current featureTags associated to active SipDelegates when metrics is pulled */
+ public synchronized void conclude(long timeStamp) {
+ HashMap<String, LastFeatureTagState> featureTagsCopy = new HashMap<>();
+ featureTagsCopy.putAll(mFeatureTagMap);
+ for (Map.Entry<String, LastFeatureTagState> last : featureTagsCopy.entrySet()) {
+ String tagName = last.getKey();
+ LastFeatureTagState lastFeatureTagState = last.getValue();
+ addFeatureTagStat(tagName, lastFeatureTagState, timeStamp);
+ updateTimeStamp(mSubId, tagName, timeStamp);
+ }
+ }
+
+ /** Finalizes the durations of the current featureTags associated to active SipDelegates */
+ private synchronized boolean addFeatureTagStat(@NonNull String tagName,
+ @NonNull LastFeatureTagState lastFeatureTagState, long now) {
+ long duration = now - lastFeatureTagState.timeStamp;
+ if (duration < MIN_DURATION_MILLIS
+ || !isValidCarrierId(lastFeatureTagState.carrierId)) {
+ logd("conclude: discarding transient stats, duration= " + duration
+ + ", carrierId = " + lastFeatureTagState.carrierId);
+ } else {
+ SipTransportFeatureTagStats sipFeatureTagStat = new SipTransportFeatureTagStats();
+ switch (lastFeatureTagState.state) {
+ case STATE_DENIED:
+ sipFeatureTagStat.sipTransportDeniedReason = lastFeatureTagState.reason;
+ sipFeatureTagStat.sipTransportDeregisteredReason = NONE;
+ break;
+ case STATE_DEREGISTERED:
+ sipFeatureTagStat.sipTransportDeniedReason = NONE;
+ sipFeatureTagStat.sipTransportDeregisteredReason =
+ lastFeatureTagState.reason;
+ break;
+ default:
+ sipFeatureTagStat.sipTransportDeniedReason = NONE;
+ sipFeatureTagStat.sipTransportDeregisteredReason = NONE;
+ break;
+ }
+
+ sipFeatureTagStat.carrierId = lastFeatureTagState.carrierId;
+ sipFeatureTagStat.slotId = lastFeatureTagState.slotId;
+ sipFeatureTagStat.associatedMillis = duration;
+ sipFeatureTagStat.featureTagName = convertTagNameToValue(tagName);
+ mAtomsStorage.addSipTransportFeatureTagStats(sipFeatureTagStat);
+ return true;
+ }
+ return false;
+ }
+
+ private void updateTimeStamp(int subId, String tagName, long timeStamp) {
+ SipTransportFeatureTags sipTransportFeatureTags = mLastFeatureTagStatMap.get(subId);
+ if (sipTransportFeatureTags != null) {
+ HashMap<String, LastFeatureTagState> lastTagStates =
+ sipTransportFeatureTags.getLastTagStates();
+ if (lastTagStates != null && lastTagStates.containsKey(tagName)) {
+ LastFeatureTagState lastFeatureTagState = lastTagStates.get(tagName);
+ if (lastFeatureTagState != null) {
+ lastFeatureTagState.update(timeStamp);
+ }
+ }
+ }
+ }
+
+ private LastFeatureTagState create(String tagName, int carrierId, int slotId, int state,
+ int reason, long timeStamp) {
+ LastFeatureTagState lastFeatureTagState = new LastFeatureTagState(carrierId, slotId,
+ state, reason, timeStamp);
+ mFeatureTagMap.put(tagName, lastFeatureTagState);
+ return lastFeatureTagState;
+ }
+ }
+
+ class UceStatsWriterCallback implements UceStatsCallback {
+ private RcsStats mRcsStats;
+
+ UceStatsWriterCallback(RcsStats rcsStats) {
+ logd("created Callback");
+ mRcsStats = rcsStats;
+ }
+
+ public void onImsRegistrationFeatureTagStats(int subId, List<String> featureTagList,
+ int registrationTech) {
+ mRcsStats.onImsRegistrationFeatureTagStats(subId, featureTagList, registrationTech);
+ }
+
+ public void onStoreCompleteImsRegistrationFeatureTagStats(int subId) {
+ mRcsStats.onStoreCompleteImsRegistrationFeatureTagStats(subId);
+ }
+
+ public void onImsRegistrationServiceDescStats(int subId, List<String> serviceIdList,
+ List<String> serviceIdVersionList, int registrationTech) {
+ mRcsStats.onImsRegistrationServiceDescStats(subId, serviceIdList, serviceIdVersionList,
+ registrationTech);
+ }
+
+ public void onSubscribeResponse(int subId, long taskId, int networkResponse) {
+ if (networkResponse >= 200 && networkResponse <= 299) {
+ if (!sSubscribeTaskIds.containsKey(taskId)) {
+ sSubscribeTaskIds.put(taskId, SUBSCRIBE_SUCCESS);
+ }
+ }
+ mRcsStats.onUceEventStats(subId, UCE_EVENT_STATS__TYPE__SUBSCRIBE,
+ true, 0, networkResponse);
+ }
+
+ public void onUceEvent(int subId, int type, boolean successful, int commandCode,
+ int networkResponse) {
+ int eventType = 0;
+ switch (type) {
+ case UceStatsWriter.PUBLISH_EVENT:
+ eventType = UCE_EVENT_STATS__TYPE__PUBLISH;
+ break;
+ case UceStatsWriter.SUBSCRIBE_EVENT:
+ eventType = UCE_EVENT_STATS__TYPE__SUBSCRIBE;
+ break;
+ case UceStatsWriter.INCOMING_OPTION_EVENT:
+ eventType = UCE_EVENT_STATS__TYPE__INCOMING_OPTION;
+ break;
+ case UceStatsWriter.OUTGOING_OPTION_EVENT:
+ eventType = UCE_EVENT_STATS__TYPE__OUTGOING_OPTION;
+ break;
+ default:
+ return;
+ }
+ mRcsStats.onUceEventStats(subId, eventType, successful, commandCode, networkResponse);
+ }
+
+ public void onSubscribeTerminated(int subId, long taskId, String reason) {
+ if (sSubscribeTaskIds.containsKey(taskId)) {
+ int previousSubscribeStatus = sSubscribeTaskIds.get(taskId);
+ sSubscribeTaskIds.remove(taskId);
+ // The device received a success response related to the subscription request.
+ // However, PIDF was not received due to reason value.
+ if (previousSubscribeStatus == SUBSCRIBE_SUCCESS) {
+ mRcsStats.onPresenceNotifyEvent(subId, reason, false,
+ false, false, false);
+ }
+ }
+ }
+
+ public void onPresenceNotifyEvent(int subId, long taskId,
+ List<RcsContactUceCapability> updatedCapList) {
+ if (updatedCapList == null || updatedCapList.isEmpty()) {
+ return;
+ }
+ if (sSubscribeTaskIds.containsKey(taskId)) {
+ sSubscribeTaskIds.replace(taskId, SUBSCRIBE_NOTIFY);
+ }
+ for (RcsContactUceCapability capability : updatedCapList) {
+ boolean rcsCap = false;
+ boolean mmtelCap = false;
+ boolean noCap = true;
+ List<RcsContactPresenceTuple> tupleList = capability.getCapabilityTuples();
+ if (tupleList.isEmpty()) {
+ noCap = true;
+ mRcsStats.onPresenceNotifyEvent(subId, "", true,
+ rcsCap, mmtelCap, noCap);
+ continue;
+ }
+ for (RcsContactPresenceTuple tuple : tupleList) {
+ String serviceId = tuple.getServiceId();
+ if (RCS_SERVICE_ID_SET.contains(serviceId)) {
+ rcsCap = true;
+ noCap = false;
+ } else if (MMTEL_SERVICE_ID_SET.contains(serviceId)) {
+ if (serviceId.equals(RcsContactPresenceTuple.SERVICE_ID_CALL_COMPOSER)) {
+ if ("1.0".equals(tuple.getServiceVersion())) {
+ rcsCap = true;
+ noCap = false;
+ continue;
+ }
+ }
+ mmtelCap = true;
+ noCap = false;
+ }
+ }
+ mRcsStats.onPresenceNotifyEvent(subId, "", true, rcsCap,
+ mmtelCap, noCap);
+ }
+ }
+
+ public void onStoreCompleteImsRegistrationServiceDescStats(int subId) {
+ mRcsStats.onStoreCompleteImsRegistrationServiceDescStats(subId);
+ }
+ }
+
+ /** Callback class to receive RCS ACS result and to store metrics. */
+ public class RcsProvisioningCallback extends IRcsConfigCallback.Stub {
+ private RcsStats mRcsStats;
+ private int mSubId;
+ private boolean mEnableSingleRegistration;
+ private boolean mRegistered;
+
+ RcsProvisioningCallback(RcsStats rcsStats, int subId, boolean enableSingleRegistration) {
+ logd("created RcsProvisioningCallback");
+ mRcsStats = rcsStats;
+ mSubId = subId;
+ mEnableSingleRegistration = enableSingleRegistration;
+ mRegistered = false;
+ }
+
+ public synchronized void setEnableSingleRegistration(boolean enableSingleRegistration) {
+ mEnableSingleRegistration = enableSingleRegistration;
+ }
+
+ public boolean getRegistered() {
+ return mRegistered;
+ }
+
+ public void setRegistered(boolean registered) {
+ mRegistered = registered;
+ }
+
+ @Override
+ public void onConfigurationChanged(byte[] config) {
+ // this callback will not be handled.
+ }
+
+ @Override
+ public void onAutoConfigurationErrorReceived(int errorCode, String errorString) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mRcsStats.onRcsAcsProvisioningStats(mSubId, errorCode,
+ RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__ERROR,
+ mEnableSingleRegistration);
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public void onConfigurationReset() {
+ // this callback will not be handled.
+ }
+
+ @Override
+ public void onRemoved() {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ // store cached metrics
+ mRcsStats.onStoreCompleteRcsAcsProvisioningStats(mSubId);
+ // remove this obj from Map
+ mRcsStats.removeRcsProvisioningCallback(mSubId);
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override
+ public void onPreProvisioningReceived(byte[] config) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ // Receiving pre provisioning means http 200 OK with body.
+ mRcsStats.onRcsAcsProvisioningStats(mSubId, 200,
+ RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PRE_PROVISIONING_XML,
+ mEnableSingleRegistration);
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+ };
+
+ private class SipMessageArray {
+ private String mMethod;
+ private String mCallId;
+ private int mDirection;
+
+ SipMessageArray(String method, int direction, String callId) {
+ this.mMethod = method;
+ this.mCallId = callId;
+ this.mDirection = direction;
+ }
+
+ private synchronized void addSipMessageStat(
+ @NonNull int subId, @NonNull String sipMessageMethod,
+ int sipMessageResponse, int sipMessageDirection, int messageError) {
+ int carrierId = getCarrierId(subId);
+ if (!isValidCarrierId(carrierId)) {
+ return;
+ }
+ SipMessageResponse proto = new SipMessageResponse();
+ proto.carrierId = carrierId;
+ proto.slotId = getSlotId(subId);
+ proto.sipMessageMethod = convertMessageTypeToValue(sipMessageMethod);
+ proto.sipMessageResponse = sipMessageResponse;
+ proto.sipMessageDirection = sipMessageDirection;
+ proto.messageError = messageError;
+ proto.count = 1;
+ mAtomsStorage.addSipMessageResponse(proto);
+ }
+ }
+
+ private class SipTransportSessionArray {
+ private String mMethod;
+ private String mCallId;
+ private int mDirection;
+ private int mSipResponse;
+
+ SipTransportSessionArray(String method, int direction, String callId) {
+ this.mMethod = method;
+ this.mCallId = callId;
+ this.mDirection = direction;
+ this.mSipResponse = 0;
+ }
+
+ private synchronized void addSipTransportSessionStat(
+ @NonNull int subId, @NonNull String sessionMethod, int sipMessageDirection,
+ int sipResponse, boolean isEndedGracefully) {
+ int carrierId = getCarrierId(subId);
+ if (!isValidCarrierId(carrierId)) {
+ return;
+ }
+ SipTransportSession proto = new SipTransportSession();
+ proto.carrierId = carrierId;
+ proto.slotId = getSlotId(subId);
+ proto.sessionMethod = convertMessageTypeToValue(sessionMethod);
+ proto.sipMessageDirection = sipMessageDirection;
+ proto.sipResponse = sipResponse;
+ proto.sessionCount = 1;
+ proto.endedGracefullyCount = 1;
+ proto.isEndedGracefully = isEndedGracefully;
+ mAtomsStorage.addCompleteSipTransportSession(proto);
+ }
+ }
+
+ @VisibleForTesting
+ protected RcsStats() {
+ mCallback = null;
+ }
+
+ /** Gets a RcsStats instance. */
+ public static RcsStats getInstance() {
+ synchronized (RcsStats.class) {
+ if (sInstance == null) {
+ Rlog.d(TAG, "RcsStats created.");
+ sInstance = new RcsStats();
+ }
+ return sInstance;
+ }
+ }
+
+ /** register callback to UceStatsWriter. */
+ public void registerUceCallback() {
+ if (mCallback == null) {
+ mCallback = new UceStatsWriterCallback(sInstance);
+ Rlog.d(TAG, "UceStatsWriterCallback created.");
+ UceStatsWriter.init(mCallback);
+ }
+ }
+
+ /** Update or create new atom when RCS service registered. */
+ public void onImsRegistrationFeatureTagStats(int subId, List<String> featureTagList,
+ int registrationTech) {
+ synchronized (mImsRegistrationFeatureTagStatsList) {
+ int carrierId = getCarrierId(subId);
+ if (!isValidCarrierId(carrierId)) {
+ flushImsRegistrationFeatureTagStatsInvalid();
+ return;
+ }
+
+ // update cached atom if exists
+ onStoreCompleteImsRegistrationFeatureTagStats(subId);
+
+ if (featureTagList == null) {
+ Rlog.d(TAG, "featureTagNames is null or empty");
+ return;
+ }
+
+ for (String featureTag : featureTagList) {
+ ImsRegistrationFeatureTagStats proto = new ImsRegistrationFeatureTagStats();
+ proto.carrierId = carrierId;
+ proto.slotId = getSlotId(subId);
+ proto.featureTagName = convertTagNameToValue(featureTag);
+ proto.registrationTech = registrationTech;
+ proto.registeredMillis = getWallTimeMillis();
+ mImsRegistrationFeatureTagStatsList.add(proto);
+ }
+ }
+ }
+
+ /** Update duration, store and delete cached ImsRegistrationFeatureTagStats list to storage. */
+ public void onStoreCompleteImsRegistrationFeatureTagStats(int subId) {
+ synchronized (mImsRegistrationFeatureTagStatsList) {
+ int carrierId = getCarrierId(subId);
+ List<ImsRegistrationFeatureTagStats> deleteList = new ArrayList<>();
+ long now = getWallTimeMillis();
+ for (ImsRegistrationFeatureTagStats proto : mImsRegistrationFeatureTagStatsList) {
+ if (proto.carrierId == carrierId) {
+ proto.registeredMillis = now - proto.registeredMillis;
+ mAtomsStorage.addImsRegistrationFeatureTagStats(proto);
+ deleteList.add(proto);
+ }
+ }
+ for (ImsRegistrationFeatureTagStats proto : deleteList) {
+ mImsRegistrationFeatureTagStatsList.remove(proto);
+ }
+ }
+ }
+
+ /** Update duration and store cached ImsRegistrationFeatureTagStats when metrics are pulled */
+ public void onFlushIncompleteImsRegistrationFeatureTagStats() {
+ synchronized (mImsRegistrationFeatureTagStatsList) {
+ long now = getWallTimeMillis();
+ for (ImsRegistrationFeatureTagStats proto : mImsRegistrationFeatureTagStatsList) {
+ ImsRegistrationFeatureTagStats newProto = copyImsRegistrationFeatureTagStats(proto);
+ // the current time is a placeholder and total registered time will be
+ // calculated when generating final atoms
+ newProto.registeredMillis = now - proto.registeredMillis;
+ mAtomsStorage.addImsRegistrationFeatureTagStats(newProto);
+ proto.registeredMillis = now;
+ }
+ }
+ }
+
+ /** Create a new atom when RCS client stat changed. */
+ public synchronized void onRcsClientProvisioningStats(int subId, int event) {
+ int carrierId = getCarrierId(subId);
+
+ if (!isValidCarrierId(carrierId)) {
+ return;
+ }
+
+ RcsClientProvisioningStats proto = new RcsClientProvisioningStats();
+ proto.carrierId = carrierId;
+ proto.slotId = getSlotId(subId);
+ proto.event = event;
+ proto.count = 1;
+ mAtomsStorage.addRcsClientProvisioningStats(proto);
+ }
+
+ /** Update or create new atom when RCS ACS stat changed. */
+ public void onRcsAcsProvisioningStats(int subId, int responseCode, int responseType,
+ boolean enableSingleRegistration) {
+
+ synchronized (mRcsAcsProvisioningStatsList) {
+ int carrierId = getCarrierId(subId);
+ if (!isValidCarrierId(carrierId)) {
+ flushRcsAcsProvisioningStatsInvalid();
+ return;
+ }
+
+ // update cached atom if exists
+ onStoreCompleteRcsAcsProvisioningStats(subId);
+
+ // create new stats to cache
+ RcsAcsProvisioningStats newStats = new RcsAcsProvisioningStats();
+ newStats.carrierId = carrierId;
+ newStats.slotId = getSlotId(subId);
+ newStats.responseCode = responseCode;
+ newStats.responseType = responseType;
+ newStats.isSingleRegistrationEnabled = enableSingleRegistration;
+ newStats.count = 1;
+ newStats.stateTimerMillis = getWallTimeMillis();
+
+ // add new stats in list
+ mRcsAcsProvisioningStatsList.add(newStats);
+ }
+ }
+
+ /** Update duration, store and delete cached RcsAcsProvisioningStats */
+ public void onStoreCompleteRcsAcsProvisioningStats(int subId) {
+ synchronized (mRcsAcsProvisioningStatsList) {
+ // find cached RcsAcsProvisioningStats based sub ID
+ RcsAcsProvisioningStats existingStats = getRcsAcsProvisioningStats(subId);
+ if (existingStats != null) {
+ existingStats.stateTimerMillis =
+ getWallTimeMillis() - existingStats.stateTimerMillis;
+ mAtomsStorage.addRcsAcsProvisioningStats(existingStats);
+ // remove cached atom from list
+ mRcsAcsProvisioningStatsList.remove(existingStats);
+ }
+ }
+ }
+
+ /** Update duration and store cached RcsAcsProvisioningStats when metrics are pulled */
+ public void onFlushIncompleteRcsAcsProvisioningStats() {
+ synchronized (mRcsAcsProvisioningStatsList) {
+ long now = getWallTimeMillis();
+ for (RcsAcsProvisioningStats stats : mRcsAcsProvisioningStatsList) {
+ // we store a copy into atoms storage
+ // so that we can continue using the original object.
+ RcsAcsProvisioningStats proto = copyRcsAcsProvisioningStats(stats);
+ // the current time is a placeholder and total registered time will be
+ // calculated when generating final atoms
+ proto.stateTimerMillis = now - proto.stateTimerMillis;
+ mAtomsStorage.addRcsAcsProvisioningStats(proto);
+ // update cached atom's time
+ stats.stateTimerMillis = now;
+ }
+ }
+ }
+
+ /** Create SipDelegateStat when SipDelegate is created */
+ public synchronized void createSipDelegateStats(int subId, Set<String> supportedTags) {
+ if (supportedTags != null && !supportedTags.isEmpty()) {
+ LastSipDelegateStat lastState = getLastSipDelegateStat(subId, supportedTags);
+ lastState.createSipDelegateStat(subId);
+ }
+ }
+
+ /** Update destroyReason and duration of SipDelegateStat when SipDelegate is destroyed */
+ public synchronized void onSipDelegateStats(int subId, Set<String> supportedTags,
+ int destroyReason) {
+ if (supportedTags != null && !supportedTags.isEmpty()) {
+ LastSipDelegateStat lastState = getLastSipDelegateStat(subId, supportedTags);
+ lastState.setSipDelegateDestroyReason(destroyReason);
+ concludeSipDelegateStat();
+ }
+ }
+
+ /** Create/Update atoms when states of sipTransportFeatureTags are changed */
+ public synchronized void onSipTransportFeatureTagStats(
+ int subId,
+ Set<FeatureTagState> deniedTags,
+ Set<FeatureTagState> deRegiTags,
+ Set<String> regiTags) {
+ long now = getWallTimeMillis();
+ SipTransportFeatureTags sipTransportFeatureTags = getLastFeatureTags(subId);
+ if (regiTags != null && !regiTags.isEmpty()) {
+ for (String tag : regiTags) {
+ sipTransportFeatureTags.updateLastFeatureTagState(tag, STATE_REGISTERED,
+ NONE, now);
+ }
+ }
+ if (deniedTags != null && !deniedTags.isEmpty()) {
+ for (FeatureTagState tag : deniedTags) {
+ sipTransportFeatureTags.updateLastFeatureTagState(tag.getFeatureTag(), STATE_DENIED,
+ tag.getState(), now);
+ }
+ }
+ if (deRegiTags != null && !deRegiTags.isEmpty()) {
+ for (FeatureTagState tag : deRegiTags) {
+ sipTransportFeatureTags.updateLastFeatureTagState(
+ tag.getFeatureTag(), STATE_DEREGISTERED, tag.getState(), now);
+ }
+ }
+ }
+
+ /** Update duration of sipTransportFeatureTags when metrics are pulled */
+ public synchronized void concludeSipTransportFeatureTagsStat() {
+ if (mLastFeatureTagStatMap.isEmpty()) {
+ return;
+ }
+
+ long now = getWallTimeMillis();
+ HashMap<Integer, SipTransportFeatureTags> lastFeatureTagStatsCopy = new HashMap<>();
+ lastFeatureTagStatsCopy.putAll(mLastFeatureTagStatMap);
+ for (SipTransportFeatureTags sipTransportFeatureTags : lastFeatureTagStatsCopy.values()) {
+ if (sipTransportFeatureTags != null) {
+ sipTransportFeatureTags.conclude(now);
+ }
+ }
+ }
+
+ /** Request Message */
+ public synchronized void onSipMessageRequest(String callId, String sipMessageMethod,
+ int sipMessageDirection) {
+ mSipMessage = new SipMessageArray(sipMessageMethod, sipMessageDirection, callId);
+ mSipMessageArray.add(mSipMessage);
+ }
+
+ /** invalidated result when Request message is sent */
+ public synchronized void invalidatedMessageResult(int subId, String sipMessageMethod,
+ int sipMessageDirection, int messageError) {
+ mSipMessage.addSipMessageStat(subId, sipMessageMethod, 0,
+ sipMessageDirection, messageError);
+ }
+
+ /** Create a new atom when RCS SIP Message Response changed. */
+ public synchronized void onSipMessageResponse(int subId, String callId,
+ int sipMessageResponse, int messageError) {
+ SipMessageArray match = mSipMessageArray.stream()
+ .filter(d -> d.mCallId.equals(callId)).findFirst().orElse(null);
+ if (match != null) {
+ mSipMessage.addSipMessageStat(subId, match.mMethod, sipMessageResponse,
+ match.mDirection, messageError);
+ mSipMessageArray.removeIf(d -> d.mCallId.equals(callId));
+ }
+ }
+
+ /** Request SIP Method Message */
+ public synchronized void earlySipTransportSession(String sessionMethod, String callId,
+ int sipMessageDirection) {
+ mSipTransportSession = new SipTransportSessionArray(sessionMethod,
+ sipMessageDirection, callId);
+ mSipTransportSessionArray.add(mSipTransportSession);
+ }
+
+ /** Response Message */
+ public synchronized void confirmedSipTransportSession(String callId,
+ int sipResponse) {
+ SipTransportSessionArray match = mSipTransportSessionArray.stream()
+ .filter(d -> d.mCallId.equals(callId)).findFirst().orElse(null);
+ if (match != null) {
+ match.mSipResponse = sipResponse;
+ }
+ }
+
+ /** Create a new atom when RCS SIP Transport Session changed. */
+ public synchronized void onSipTransportSessionClosed(int subId, String callId,
+ int sipResponse, boolean isEndedGracefully) {
+ SipTransportSessionArray match = mSipTransportSessionArray.stream()
+ .filter(d -> d.mCallId.equals(callId)).findFirst().orElse(null);
+ if (match != null) {
+ if (sipResponse != 0) {
+ match.mSipResponse = sipResponse;
+ }
+ mSipTransportSession.addSipTransportSessionStat(subId, match.mMethod, match.mDirection,
+ sipResponse, isEndedGracefully);
+ mSipTransportSessionArray.removeIf(d -> d.mCallId.equals(callId));
+ }
+ }
+
+ /** Add a listener to the hashmap for waiting upcoming DedicatedBearer established event */
+ public synchronized void onImsDedicatedBearerListenerAdded(@NonNull final int listenerId,
+ @NonNull final int slotId, @NonNull final int ratAtEnd, @NonNull final int qci) {
+ int subId = getSubId(slotId);
+ int carrierId = getCarrierId(subId);
+
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ || !isValidCarrierId(carrierId)) {
+ return;
+ }
+
+ if (mDedicatedBearerListenerEventMap.containsKey(listenerId)) {
+ return;
+ }
+
+ ImsDedicatedBearerListenerEvent preProto = new ImsDedicatedBearerListenerEvent();
+ preProto.carrierId = carrierId;
+ preProto.slotId = slotId;
+ preProto.ratAtEnd = ratAtEnd;
+ preProto.qci = qci;
+ preProto.dedicatedBearerEstablished = false;
+ preProto.eventCount = 1;
+
+ mDedicatedBearerListenerEventMap.put(listenerId, preProto);
+ }
+
+ /** update previously added atom with dedicatedBearerEstablished = true when
+ * DedicatedBearerListener Event changed. */
+ public synchronized void onImsDedicatedBearerListenerUpdateSession(final int listenerId,
+ final int slotId, final int rat, final int qci,
+ @NonNull final boolean dedicatedBearerEstablished) {
+ int subId = getSubId(slotId);
+ int carrierId = getCarrierId(subId);
+
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ || !isValidCarrierId(carrierId)) {
+ return;
+ }
+
+ if (mDedicatedBearerListenerEventMap.containsKey(listenerId)) {
+ ImsDedicatedBearerListenerEvent preProto =
+ mDedicatedBearerListenerEventMap.get(listenerId);
+
+ preProto.ratAtEnd = rat;
+ preProto.qci = qci;
+ preProto.dedicatedBearerEstablished = dedicatedBearerEstablished;
+
+ mDedicatedBearerListenerEventMap.replace(listenerId, preProto);
+ } else {
+ ImsDedicatedBearerListenerEvent preProto = new ImsDedicatedBearerListenerEvent();
+ preProto.carrierId = carrierId;
+ preProto.slotId = slotId;
+ preProto.ratAtEnd = rat;
+ preProto.qci = qci;
+ preProto.dedicatedBearerEstablished = dedicatedBearerEstablished;
+ preProto.eventCount = 1;
+
+ mDedicatedBearerListenerEventMap.put(listenerId, preProto);
+ }
+ }
+
+ /** add proto to atom when listener is removed, so that I can save the status of dedicatedbearer
+ * establishment per listener id */
+ public synchronized void onImsDedicatedBearerListenerRemoved(@NonNull final int listenerId) {
+ if (mDedicatedBearerListenerEventMap.containsKey(listenerId)) {
+
+ ImsDedicatedBearerListenerEvent newProto =
+ mDedicatedBearerListenerEventMap.get(listenerId);
+
+ mAtomsStorage.addImsDedicatedBearerListenerEvent(newProto);
+ mDedicatedBearerListenerEventMap.remove(listenerId);
+ }
+ }
+
+ /** Create a new atom when DedicatedBearer Event changed. */
+ public synchronized void onImsDedicatedBearerEvent(int slotId, int ratAtEnd, int qci,
+ int bearerState, boolean localConnectionInfoReceived,
+ boolean remoteConnectionInfoReceived, boolean hasListeners) {
+ int subId = getSubId(slotId);
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ return;
+ }
+
+ ImsDedicatedBearerEvent proto = new ImsDedicatedBearerEvent();
+ proto.carrierId = getCarrierId(subId);
+ proto.slotId = getSlotId(subId);
+ proto.ratAtEnd = ratAtEnd;
+ proto.qci = qci;
+ proto.bearerState = bearerState;
+ proto.localConnectionInfoReceived = localConnectionInfoReceived;
+ proto.remoteConnectionInfoReceived = remoteConnectionInfoReceived;
+ proto.hasListeners = hasListeners;
+ proto.count = 1;
+ mAtomsStorage.addImsDedicatedBearerEvent(proto);
+ }
+
+ /**
+ * Update or Create a new atom when Ims Registration Service Desc state changed.
+ * Use-related parts are already converted from UseStatsWriter based on RcsContactPresenceTuple.
+ */
+ public void onImsRegistrationServiceDescStats(int subId, List<String> serviceIdList,
+ List<String> serviceIdVersionList, int registrationTech) {
+ synchronized (mImsRegistrationServiceDescStatsList) {
+ int carrierId = getCarrierId(subId);
+ if (!isValidCarrierId(carrierId)) {
+ handleImsRegistrationServiceDescStats();
+ return;
+ }
+ // update cached atom if exists
+ onStoreCompleteImsRegistrationServiceDescStats(subId);
+
+ if (serviceIdList == null) {
+ Rlog.d(TAG, "serviceIds is null or empty");
+ return;
+ }
+
+ int index = 0;
+ for (String serviceId : serviceIdList) {
+ ImsRegistrationServiceDescStats mImsRegistrationServiceDescStats =
+ new ImsRegistrationServiceDescStats();
+
+ mImsRegistrationServiceDescStats.carrierId = carrierId;
+ mImsRegistrationServiceDescStats.slotId = getSlotId(subId);
+ mImsRegistrationServiceDescStats.serviceIdName = convertServiceIdToValue(serviceId);
+ mImsRegistrationServiceDescStats.serviceIdVersion =
+ Float.parseFloat(serviceIdVersionList.get(index++));
+ mImsRegistrationServiceDescStats.registrationTech = registrationTech;
+ mImsRegistrationServiceDescStatsList.add(mImsRegistrationServiceDescStats);
+ }
+ }
+ }
+
+ /** Update duration and cached of ImsRegistrationServiceDescStats when metrics are pulled */
+ public void onFlushIncompleteImsRegistrationServiceDescStats() {
+ synchronized (mImsRegistrationServiceDescStatsList) {
+ for (ImsRegistrationServiceDescStats proto : mImsRegistrationServiceDescStatsList) {
+ ImsRegistrationServiceDescStats newProto =
+ copyImsRegistrationServiceDescStats(proto);
+ long now = getWallTimeMillis();
+ // the current time is a placeholder and total registered time will be
+ // calculated when generating final atoms
+ newProto.publishedMillis = now - proto.publishedMillis;
+ mAtomsStorage.addImsRegistrationServiceDescStats(newProto);
+ proto.publishedMillis = now;
+ }
+ }
+ }
+
+ /** Create a new atom when Uce Event Stats changed. */
+ public synchronized void onUceEventStats(int subId, int type, boolean successful,
+ int commandCode, int networkResponse) {
+ UceEventStats proto = new UceEventStats();
+
+ int carrierId = getCarrierId(subId);
+ if (!isValidCarrierId(carrierId)) {
+ handleImsRegistrationServiceDescStats();
+ return;
+ }
+ proto.carrierId = carrierId;
+ proto.slotId = getSlotId(subId);
+ proto.type = type;
+ proto.successful = successful;
+ proto.commandCode = commandCode;
+ proto.networkResponse = networkResponse;
+ proto.count = 1;
+ mAtomsStorage.addUceEventStats(proto);
+
+ /**
+ * The publishedMillis of ImsRegistrationServiceDescStat is the time gap between
+ * Publish success and Un publish.
+ * So, when the publish operation is successful, the corresponding time gap is set,
+ * and in case of failure, the cached stat is deleted.
+ */
+ if (type == UCE_EVENT_STATS__TYPE__PUBLISH) {
+ if (successful) {
+ setImsRegistrationServiceDescStatsTime(proto.carrierId);
+ } else {
+ deleteImsRegistrationServiceDescStats(proto.carrierId);
+ }
+ }
+ }
+
+ /** Create a new atom when Presence Notify Event changed. */
+ public synchronized void onPresenceNotifyEvent(int subId, String reason,
+ boolean contentBodyReceived, boolean rcsCaps, boolean mmtelCaps, boolean noCaps) {
+ PresenceNotifyEvent proto = new PresenceNotifyEvent();
+
+ int carrierId = getCarrierId(subId);
+ if (!isValidCarrierId(carrierId)) {
+ handleImsRegistrationServiceDescStats();
+ return;
+ }
+
+ proto.carrierId = carrierId;
+ proto.slotId = getSlotId(subId);
+ proto.reason = convertPresenceNotifyReason(reason);
+ proto.contentBodyReceived = contentBodyReceived;
+ proto.rcsCapsCount = rcsCaps ? 1 : 0;
+ proto.mmtelCapsCount = mmtelCaps ? 1 : 0;
+ proto.noCapsCount = noCaps ? 1 : 0;
+ proto.count = 1;
+ mAtomsStorage.addPresenceNotifyEvent(proto);
+ }
+
+ /** Update duration a created Ims Registration Desc Stat atom when Un publish event happened. */
+ public void onStoreCompleteImsRegistrationServiceDescStats(int subId) {
+ synchronized (mImsRegistrationServiceDescStatsList) {
+ int carrierId = getCarrierId(subId);
+ List<ImsRegistrationServiceDescStats> deleteList = new ArrayList<>();
+ for (ImsRegistrationServiceDescStats proto : mImsRegistrationServiceDescStatsList) {
+ if (proto.carrierId == carrierId) {
+ proto.publishedMillis = getWallTimeMillis() - proto.publishedMillis;
+ mAtomsStorage.addImsRegistrationServiceDescStats(proto);
+ deleteList.add(proto);
+ }
+ }
+ for (ImsRegistrationServiceDescStats proto : deleteList) {
+ mImsRegistrationServiceDescStatsList.remove(proto);
+ }
+ }
+ }
+
+ /** Create a new atom when GBA Success Event changed. */
+ public synchronized void onGbaSuccessEvent(int subId) {
+ int carrierId = getCarrierId(subId);
+ if (!isValidCarrierId(carrierId)) {
+ return;
+ }
+
+ GbaEvent proto = new GbaEvent();
+ proto.carrierId = carrierId;
+ proto.slotId = getSlotId(subId);
+ proto.successful = true;
+ proto.failedReason = -1;
+ proto.count = 1;
+ mAtomsStorage.addGbaEvent(proto);
+ }
+
+ /** Create a new atom when GBA Failure Event changed. */
+ public synchronized void onGbaFailureEvent(int subId, int reason) {
+ int carrierId = getCarrierId(subId);
+ if (!isValidCarrierId(carrierId)) {
+ return;
+ }
+
+ GbaEvent proto = new GbaEvent();
+ proto.carrierId = carrierId;
+ proto.slotId = getSlotId(subId);
+ proto.successful = false;
+ proto.failedReason = reason;
+ proto.count = 1;
+ mAtomsStorage.addGbaEvent(proto);
+ }
+
+ /** Create or return exist RcsProvisioningCallback based on subId. */
+ public synchronized RcsProvisioningCallback getRcsProvisioningCallback(int subId,
+ boolean enableSingleRegistration) {
+ // find exist obj in Map
+ RcsProvisioningCallback rcsProvisioningCallback = mRcsProvisioningCallbackMap.get(subId);
+ if (rcsProvisioningCallback != null) {
+ return rcsProvisioningCallback;
+ }
+
+ // create new, add Map and return
+ rcsProvisioningCallback = new RcsProvisioningCallback(this, subId,
+ enableSingleRegistration);
+ mRcsProvisioningCallbackMap.put(subId, rcsProvisioningCallback);
+ return rcsProvisioningCallback;
+ }
+
+ /** Set whether single registration is supported. */
+ public synchronized void setEnableSingleRegistration(int subId,
+ boolean enableSingleRegistration) {
+ // find exist obj and set
+ RcsProvisioningCallback callbackBinder = mRcsProvisioningCallbackMap.get(subId);
+ if (callbackBinder != null) {
+ callbackBinder.setEnableSingleRegistration(enableSingleRegistration);
+ }
+ }
+
+ private synchronized void removeRcsProvisioningCallback(int subId) {
+ // remove obj from Map based on subId
+ mRcsProvisioningCallbackMap.remove(subId);
+ }
+
+ private ImsRegistrationFeatureTagStats copyImsRegistrationFeatureTagStats(
+ ImsRegistrationFeatureTagStats proto) {
+ ImsRegistrationFeatureTagStats newProto = new ImsRegistrationFeatureTagStats();
+ newProto.carrierId = proto.carrierId;
+ newProto.slotId = proto.slotId;
+ newProto.featureTagName = proto.featureTagName;
+ newProto.registrationTech = proto.registrationTech;
+ newProto.registeredMillis = proto.registeredMillis;
+
+ return newProto;
+ }
+
+ private RcsAcsProvisioningStats copyRcsAcsProvisioningStats(RcsAcsProvisioningStats proto) {
+ RcsAcsProvisioningStats newProto = new RcsAcsProvisioningStats();
+ newProto.carrierId = proto.carrierId;
+ newProto.slotId = proto.slotId;
+ newProto.responseCode = proto.responseCode;
+ newProto.responseType = proto.responseType;
+ newProto.isSingleRegistrationEnabled = proto.isSingleRegistrationEnabled;
+ newProto.count = proto.count;
+ newProto.stateTimerMillis = proto.stateTimerMillis;
+
+ return newProto;
+ }
+
+ private ImsRegistrationServiceDescStats copyImsRegistrationServiceDescStats(
+ ImsRegistrationServiceDescStats proto) {
+ ImsRegistrationServiceDescStats newProto = new ImsRegistrationServiceDescStats();
+ newProto.carrierId = proto.carrierId;
+ newProto.slotId = proto.slotId;
+ newProto.serviceIdName = proto.serviceIdName;
+ newProto.serviceIdVersion = proto.serviceIdVersion;
+ newProto.registrationTech = proto.registrationTech;
+ return newProto;
+ }
+
+ private void setImsRegistrationServiceDescStatsTime(int carrierId) {
+ synchronized (mImsRegistrationServiceDescStatsList) {
+ for (ImsRegistrationServiceDescStats descStats : mImsRegistrationServiceDescStatsList) {
+ if (descStats.carrierId == carrierId) {
+ descStats.publishedMillis = getWallTimeMillis();
+ }
+ }
+ }
+ }
+
+ private void deleteImsRegistrationServiceDescStats(int carrierId) {
+ synchronized (mImsRegistrationServiceDescStatsList) {
+ List<ImsRegistrationServiceDescStats> deleteList = new ArrayList<>();
+ for (ImsRegistrationServiceDescStats proto : mImsRegistrationServiceDescStatsList) {
+ if (proto.carrierId == carrierId) {
+ deleteList.add(proto);
+ }
+ }
+ for (ImsRegistrationServiceDescStats stats : deleteList) {
+ mImsRegistrationServiceDescStatsList.remove(stats);
+ }
+ }
+ }
+
+ private void handleImsRegistrationServiceDescStats() {
+ synchronized (mImsRegistrationServiceDescStatsList) {
+ List<ImsRegistrationServiceDescStats> deleteList = new ArrayList<>();
+ for (ImsRegistrationServiceDescStats proto : mImsRegistrationServiceDescStatsList) {
+ int subId = getSubId(proto.slotId);
+ int newCarrierId = getCarrierId(subId);
+ if (proto.carrierId != newCarrierId) {
+ deleteList.add(proto);
+ if (proto.publishedMillis != 0) {
+ proto.publishedMillis = getWallTimeMillis() - proto.publishedMillis;
+ mAtomsStorage.addImsRegistrationServiceDescStats(proto);
+ }
+ }
+ }
+ for (ImsRegistrationServiceDescStats stats : deleteList) {
+ mImsRegistrationServiceDescStatsList.remove(stats);
+ }
+ }
+ }
+
+ private RcsAcsProvisioningStats getRcsAcsProvisioningStats(int subId) {
+ int carrierId = getCarrierId(subId);
+ int slotId = getSlotId(subId);
+
+ for (RcsAcsProvisioningStats stats : mRcsAcsProvisioningStatsList) {
+ if (stats == null) {
+ continue;
+ }
+ if (stats.carrierId == carrierId && stats.slotId == slotId) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ private void flushRcsAcsProvisioningStatsInvalid() {
+ List<RcsAcsProvisioningStats> inValidList = new ArrayList<RcsAcsProvisioningStats>();
+
+ int subId;
+ int newCarrierId;
+
+ for (RcsAcsProvisioningStats stats : mRcsAcsProvisioningStatsList) {
+ subId = getSubId(stats.slotId);
+ newCarrierId = getCarrierId(subId);
+ if (stats.carrierId != newCarrierId) {
+ inValidList.add(stats);
+ }
+ }
+
+ for (RcsAcsProvisioningStats inValid : inValidList) {
+ inValid.stateTimerMillis = getWallTimeMillis() - inValid.stateTimerMillis;
+ mAtomsStorage.addRcsAcsProvisioningStats(inValid);
+ mRcsAcsProvisioningStatsList.remove(inValid);
+ }
+ inValidList.clear();
+ }
+
+ private void flushImsRegistrationFeatureTagStatsInvalid() {
+ List<ImsRegistrationFeatureTagStats> inValidList =
+ new ArrayList<ImsRegistrationFeatureTagStats>();
+
+ int subId;
+ int newCarrierId;
+
+ for (ImsRegistrationFeatureTagStats stats : mImsRegistrationFeatureTagStatsList) {
+ subId = getSubId(stats.slotId);
+ newCarrierId = getCarrierId(subId);
+ if (stats.carrierId != newCarrierId) {
+ inValidList.add(stats);
+ }
+ }
+
+ for (ImsRegistrationFeatureTagStats inValid : inValidList) {
+ inValid.registeredMillis = getWallTimeMillis() - inValid.registeredMillis;
+ mAtomsStorage.addImsRegistrationFeatureTagStats(inValid);
+ mImsRegistrationFeatureTagStatsList.remove(inValid);
+ }
+ inValidList.clear();
+ }
+
+ private LastSipDelegateStat getLastSipDelegateStat(int subId, Set<String> supportedTags) {
+ LastSipDelegateStat stat = null;
+ for (LastSipDelegateStat lastStat : mLastSipDelegateStatList) {
+ if (lastStat.compare(subId, supportedTags)) {
+ stat = lastStat;
+ break;
+ }
+ }
+
+ if (stat == null) {
+ stat = new LastSipDelegateStat(subId, supportedTags);
+ mLastSipDelegateStatList.add(stat);
+ }
+
+ return stat;
+ }
+
+ private void concludeSipDelegateStat() {
+ if (mLastSipDelegateStatList.isEmpty()) {
+ return;
+ }
+ long now = getWallTimeMillis();
+ List<LastSipDelegateStat> sipDelegateStatsCopy = new ArrayList<>(mLastSipDelegateStatList);
+ for (LastSipDelegateStat stat : sipDelegateStatsCopy) {
+ if (stat.isDestroyed()) {
+ stat.conclude(now);
+ mLastSipDelegateStatList.remove(stat);
+ }
+ }
+ }
+
+ private SipTransportFeatureTags getLastFeatureTags(int subId) {
+ SipTransportFeatureTags sipTransportFeatureTags;
+ if (mLastFeatureTagStatMap.containsKey(subId)) {
+ sipTransportFeatureTags = mLastFeatureTagStatMap.get(subId);
+ } else {
+ sipTransportFeatureTags = new SipTransportFeatureTags(subId);
+ mLastFeatureTagStatMap.put(subId, sipTransportFeatureTags);
+ }
+ return sipTransportFeatureTags;
+ }
+ @VisibleForTesting
+ protected boolean isValidCarrierId(int carrierId) {
+ return carrierId > TelephonyManager.UNKNOWN_CARRIER_ID;
+ }
+
+ @VisibleForTesting
+ protected int getSlotId(int subId) {
+ return SubscriptionManager.getPhoneId(subId);
+ }
+
+ @VisibleForTesting
+ protected int getCarrierId(int subId) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ return phone != null ? phone.getCarrierId() : TelephonyManager.UNKNOWN_CARRIER_ID;
+ }
+
+ @VisibleForTesting
+ protected long getWallTimeMillis() {
+ //time in UTC, preserved across reboots, but can be adjusted e.g. by the user or NTP
+ return System.currentTimeMillis();
+ }
+
+ @VisibleForTesting
+ protected void logd(String msg) {
+ Rlog.d(TAG, msg);
+ }
+
+ @VisibleForTesting
+ protected int getSubId(int slotId) {
+ final int[] subIds = SubscriptionManager.getSubId(slotId);
+ int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ if (subIds != null && subIds.length >= 1) {
+ subId = subIds[0];
+ }
+ return subId;
+ }
+
+ /** Get a enum value from pre-defined feature tag name list */
+ @VisibleForTesting
+ public int convertTagNameToValue(@NonNull String tagName) {
+ return FEATURE_TAGS.getOrDefault(tagName.trim().toLowerCase(),
+ TelephonyProtoEnums.IMS_FEATURE_TAG_CUSTOM);
+ }
+
+ /** Get a enum value from pre-defined service id list */
+ @VisibleForTesting
+ public int convertServiceIdToValue(@NonNull String serviceId) {
+ return SERVICE_IDS.getOrDefault(serviceId.trim().toLowerCase(),
+ IMS_REGISTRATION_SERVICE_DESC_STATS__SERVICE_ID_NAME__SERVICE_ID_CUSTOM);
+ }
+
+ /** Get a enum value from pre-defined message type list */
+ @VisibleForTesting
+ public int convertMessageTypeToValue(@NonNull String messageType) {
+ return MESSAGE_TYPE.getOrDefault(messageType.trim().toLowerCase(),
+ TelephonyProtoEnums.SIP_REQUEST_CUSTOM);
+ }
+
+ /** Get a enum value from pre-defined reason list */
+ @VisibleForTesting
+ public int convertPresenceNotifyReason(@NonNull String reason) {
+ return NOTIFY_REASONS.getOrDefault(reason.trim().toLowerCase(),
+ PRESENCE_NOTIFY_EVENT__REASON__REASON_CUSTOM);
+ }
+
+ /**
+ * Print all metrics data for debugging purposes
+ *
+ * @param rawWriter Print writer
+ */
+ public synchronized void printAllMetrics(PrintWriter rawWriter) {
+ if (mAtomsStorage == null || mAtomsStorage.mAtoms == null) {
+ return;
+ }
+
+ final IndentingPrintWriter pw = new IndentingPrintWriter(rawWriter, " ");
+ PersistAtomsProto.PersistAtoms metricAtoms = mAtomsStorage.mAtoms;
+
+ pw.println("RcsStats Metrics Proto: ");
+ pw.println("------------------------------------------");
+ pw.println("ImsRegistrationFeatureTagStats:");
+ pw.increaseIndent();
+ for (ImsRegistrationFeatureTagStats stat : metricAtoms.imsRegistrationFeatureTagStats) {
+ pw.println("[" + stat.carrierId + "]"
+ + " [" + stat.slotId + "]"
+ + " Feature Tag Name = " + stat.featureTagName
+ + ", Registration Tech = " + stat.registrationTech
+ + ", Registered Duration (ms) = " + stat.registeredMillis);
+ }
+ pw.decreaseIndent();
+
+ pw.println("RcsClientProvisioningStats:");
+ pw.increaseIndent();
+ for (RcsClientProvisioningStats stat : metricAtoms.rcsClientProvisioningStats) {
+ pw.println("[" + stat.carrierId + "]"
+ + " [" + stat.slotId + "]"
+ + " Event = " + stat.event
+ + ", Count = " + stat.count);
+ }
+ pw.decreaseIndent();
+
+ pw.println("RcsAcsProvisioningStats:");
+ pw.increaseIndent();
+ for (RcsAcsProvisioningStats stat : metricAtoms.rcsAcsProvisioningStats) {
+ pw.println("[" + stat.carrierId + "]"
+ + " [" + stat.slotId + "]"
+ + " Response Code = " + stat.responseCode
+ + ", Response Type = " + stat.responseType
+ + ", Single Registration Enabled = " + stat.isSingleRegistrationEnabled
+ + ", Count = " + stat.count
+ + ", State Timer (ms) = " + stat.stateTimerMillis);
+ }
+ pw.decreaseIndent();
+
+ pw.println("SipDelegateStats:");
+ pw.increaseIndent();
+ for (SipDelegateStats stat : metricAtoms.sipDelegateStats) {
+ pw.println("[" + stat.carrierId + "]"
+ + " [" + stat.slotId + "]"
+ + " [" + stat.dimension + "]"
+ + " Destroy Reason = " + stat.destroyReason
+ + ", Uptime (ms) = " + stat.uptimeMillis);
+ }
+ pw.decreaseIndent();
+
+ pw.println("SipTransportFeatureTagStats:");
+ pw.increaseIndent();
+ for (SipTransportFeatureTagStats stat : metricAtoms.sipTransportFeatureTagStats) {
+ pw.println("[" + stat.carrierId + "]"
+ + " [" + stat.slotId + "]"
+ + " Feature Tag Name = " + stat.featureTagName
+ + ", Denied Reason = " + stat.sipTransportDeniedReason
+ + ", Deregistered Reason = " + stat.sipTransportDeregisteredReason
+ + ", Associated Time (ms) = " + stat.associatedMillis);
+ }
+ pw.decreaseIndent();
+
+ pw.println("SipMessageResponse:");
+ pw.increaseIndent();
+ for (SipMessageResponse stat : metricAtoms.sipMessageResponse) {
+ pw.println("[" + stat.carrierId + "]"
+ + " [" + stat.slotId + "]"
+ + " Message Method = " + stat.sipMessageMethod
+ + ", Response = " + stat.sipMessageResponse
+ + ", Direction = " + stat.sipMessageDirection
+ + ", Error = " + stat.messageError
+ + ", Count = " + stat.count);
+ }
+ pw.decreaseIndent();
+
+ pw.println("SipTransportSession:");
+ pw.increaseIndent();
+ for (SipTransportSession stat : metricAtoms.sipTransportSession) {
+ pw.println("[" + stat.carrierId + "]"
+ + " [" + stat.slotId + "]"
+ + " Session Method = " + stat.sessionMethod
+ + ", Direction = " + stat.sipMessageDirection
+ + ", Response = " + stat.sipResponse
+ + ", Count = " + stat.sessionCount
+ + ", GraceFully Count = " + stat.endedGracefullyCount);
+ }
+ pw.decreaseIndent();
+
+ pw.println("ImsDedicatedBearerListenerEvent:");
+ pw.increaseIndent();
+ for (ImsDedicatedBearerListenerEvent stat : metricAtoms.imsDedicatedBearerListenerEvent) {
+ pw.println("[" + stat.carrierId + "]"
+ + " [" + stat.slotId + "]"
+ + " RAT = " + stat.ratAtEnd
+ + ", QCI = " + stat.qci
+ + ", Dedicated Bearer Established = " + stat.dedicatedBearerEstablished
+ + ", Count = " + stat.eventCount);
+ }
+ pw.decreaseIndent();
+
+ pw.println("ImsDedicatedBearerEvent:");
+ pw.increaseIndent();
+ for (ImsDedicatedBearerEvent stat : metricAtoms.imsDedicatedBearerEvent) {
+ pw.println("[" + stat.carrierId + "]"
+ + " [" + stat.slotId + "]"
+ + " RAT = " + stat.ratAtEnd
+ + ", QCI = " + stat.qci
+ + ", Bearer State = " + stat.bearerState
+ + ", Local Connection Info = " + stat.localConnectionInfoReceived
+ + ", Remote Connection Info = " + stat.remoteConnectionInfoReceived
+ + ", Listener Existence = " + stat.hasListeners
+ + ", Count = " + stat.count);
+ }
+ pw.decreaseIndent();
+
+ pw.println("ImsRegistrationServiceDescStats:");
+ pw.increaseIndent();
+ for (ImsRegistrationServiceDescStats stat : metricAtoms.imsRegistrationServiceDescStats) {
+ pw.println("[" + stat.carrierId + "]"
+ + " [" + stat.slotId + "]"
+ + " Name = " + stat.serviceIdName
+ + ", Version = " + stat.serviceIdVersion
+ + ", Registration Tech = " + stat.registrationTech
+ + ", Published Time (ms) = " + stat.publishedMillis);
+ }
+ pw.decreaseIndent();
+
+ pw.println("UceEventStats:");
+ pw.increaseIndent();
+ for (UceEventStats stat : metricAtoms.uceEventStats) {
+ pw.println("[" + stat.carrierId + "]"
+ + " [" + stat.slotId + "]"
+ + " Type = " + stat.type
+ + ", Successful = " + stat.successful
+ + ", Code = " + stat.commandCode
+ + ", Response = " + stat.networkResponse
+ + ", Count = " + stat.count);
+ }
+ pw.decreaseIndent();
+
+ pw.println("PresenceNotifyEvent:");
+ pw.increaseIndent();
+ for (PresenceNotifyEvent stat : metricAtoms.presenceNotifyEvent) {
+ pw.println("[" + stat.carrierId + "]"
+ + " [" + stat.slotId + "]"
+ + " Reason = " + stat.reason
+ + ", Body = " + stat.contentBodyReceived
+ + ", RCS Count = " + stat.rcsCapsCount
+ + ", MMTEL Count = " + stat.mmtelCapsCount
+ + ", NoCaps Count = " + stat.noCapsCount
+ + ", Count = " + stat.count);
+ }
+ pw.decreaseIndent();
+
+ pw.println("GbaEvent:");
+ pw.increaseIndent();
+ for (GbaEvent stat : metricAtoms.gbaEvent) {
+ pw.println("[" + stat.carrierId + "]"
+ + " [" + stat.slotId + "]"
+ + " Successful = " + stat.successful
+ + ", Fail Reason = " + stat.failedReason
+ + ", Count = " + stat.count);
+ }
+ pw.decreaseIndent();
+ }
+
+ /**
+ * Reset all events
+ */
+ public synchronized void reset() {
+ if (mAtomsStorage == null || mAtomsStorage.mAtoms == null) {
+ return;
+ }
+
+ PersistAtomsProto.PersistAtoms metricAtoms = mAtomsStorage.mAtoms;
+
+ metricAtoms.imsRegistrationFeatureTagStats =
+ PersistAtomsProto.ImsRegistrationFeatureTagStats.emptyArray();
+ metricAtoms.rcsClientProvisioningStats =
+ PersistAtomsProto.RcsClientProvisioningStats.emptyArray();
+ metricAtoms.rcsAcsProvisioningStats =
+ PersistAtomsProto.RcsAcsProvisioningStats.emptyArray();
+ metricAtoms.sipDelegateStats = PersistAtomsProto.SipDelegateStats.emptyArray();
+ metricAtoms.sipTransportFeatureTagStats =
+ PersistAtomsProto.SipTransportFeatureTagStats.emptyArray();
+ metricAtoms.sipMessageResponse = PersistAtomsProto.SipMessageResponse.emptyArray();
+ metricAtoms.sipTransportSession = PersistAtomsProto.SipTransportSession.emptyArray();
+ metricAtoms.imsDedicatedBearerListenerEvent =
+ PersistAtomsProto.ImsDedicatedBearerListenerEvent.emptyArray();
+ metricAtoms.imsDedicatedBearerEvent =
+ PersistAtomsProto.ImsDedicatedBearerEvent.emptyArray();
+ metricAtoms.imsRegistrationServiceDescStats =
+ PersistAtomsProto.ImsRegistrationServiceDescStats.emptyArray();
+ metricAtoms.uceEventStats = PersistAtomsProto.UceEventStats.emptyArray();
+ metricAtoms.presenceNotifyEvent = PersistAtomsProto.PresenceNotifyEvent.emptyArray();
+ metricAtoms.gbaEvent = PersistAtomsProto.GbaEvent.emptyArray();
+ }
+
+ /**
+ * Convert the PersistAtomsProto into Base-64 encoded string
+ *
+ * @return Encoded string
+ */
+ public String buildLog() {
+ PersistAtomsProto.PersistAtoms log = buildProto();
+ return Base64.encodeToString(
+ PersistAtomsProto.PersistAtoms.toByteArray(log), Base64.DEFAULT);
+ }
+
+ /**
+ * Build the PersistAtomsProto
+ *
+ * @return PersistAtomsProto.PersistAtoms
+ */
+ public PersistAtomsProto.PersistAtoms buildProto() {
+ PersistAtomsProto.PersistAtoms log = new PersistAtomsProto.PersistAtoms();
+
+ PersistAtomsProto.PersistAtoms atoms = mAtomsStorage.mAtoms;
+ log.imsRegistrationFeatureTagStats = Arrays.copyOf(atoms.imsRegistrationFeatureTagStats,
+ atoms.imsRegistrationFeatureTagStats.length);
+ log.rcsClientProvisioningStats = Arrays.copyOf(atoms.rcsClientProvisioningStats,
+ atoms.rcsClientProvisioningStats.length);
+ log.rcsAcsProvisioningStats = Arrays.copyOf(atoms.rcsAcsProvisioningStats,
+ atoms.rcsAcsProvisioningStats.length);
+ log.sipDelegateStats = Arrays.copyOf(atoms.sipDelegateStats, atoms.sipDelegateStats.length);
+ log.sipTransportFeatureTagStats = Arrays.copyOf(atoms.sipTransportFeatureTagStats,
+ atoms.sipTransportFeatureTagStats.length);
+ log.sipMessageResponse = Arrays.copyOf(atoms.sipMessageResponse,
+ atoms.sipMessageResponse.length);
+ log.sipTransportSession = Arrays.copyOf(atoms.sipTransportSession,
+ atoms.sipTransportSession.length);
+ log.imsDedicatedBearerListenerEvent = Arrays.copyOf(atoms.imsDedicatedBearerListenerEvent,
+ atoms.imsDedicatedBearerListenerEvent.length);
+ log.imsDedicatedBearerEvent = Arrays.copyOf(atoms.imsDedicatedBearerEvent,
+ atoms.imsDedicatedBearerEvent.length);
+ log.imsRegistrationServiceDescStats = Arrays.copyOf(atoms.imsRegistrationServiceDescStats,
+ atoms.imsRegistrationServiceDescStats.length);
+ log.uceEventStats = Arrays.copyOf(atoms.uceEventStats, atoms.uceEventStats.length);
+ log.presenceNotifyEvent = Arrays.copyOf(atoms.presenceNotifyEvent,
+ atoms.presenceNotifyEvent.length);
+ log.gbaEvent = Arrays.copyOf(atoms.gbaEvent, atoms.gbaEvent.length);
+
+ return log;
+ }
+
+}
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index 5e43876..7028d95 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -290,12 +290,14 @@
break;
case "--metricsproto":
pw.println(convertProtoToBase64String(buildProto()));
+ pw.println(RcsStats.getInstance().buildLog());
if (reset) {
reset();
}
break;
case "--metricsprototext":
pw.println(buildProto().toString());
+ pw.println(RcsStats.getInstance().buildProto().toString());
break;
}
}
@@ -644,6 +646,8 @@
for (BwEstimationStats stats : mBwEstStatsMapList.get(1).values()) {
pw.println(stats.toString());
}
+
+ RcsStats.getInstance().printAllMetrics(rawWriter);
}
/**
@@ -745,6 +749,8 @@
.setRadioState(mLastRadioState.get(key)).build();
addTelephonyEvent(event);
}
+
+ RcsStats.getInstance().reset();
}
/**
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java
index 8939977..8803984 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GbaManagerTest.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import static com.android.internal.telephony.TelephonyStatsLog.GBA_EVENT__FAILED_REASON__FEATURE_NOT_READY;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -50,6 +52,8 @@
import android.testing.TestableLooper;
import android.util.Log;
+import com.android.internal.telephony.metrics.RcsStats;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -79,6 +83,7 @@
@Mock IBinder mMockBinder;
@Mock IGbaService mMockGbaServiceBinder;
@Mock IBootstrapAuthenticationCallback mMockCallback;
+ @Mock RcsStats mMockRcsStats;
private GbaManager mTestGbaManager;
private Handler mHandler;
private TestableLooper mLooper;
@@ -92,7 +97,7 @@
}
when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
when(mMockGbaServiceBinder.asBinder()).thenReturn(mMockBinder);
- mTestGbaManager = new GbaManager(mMockContext, TEST_SUB_ID, null, 0);
+ mTestGbaManager = new GbaManager(mMockContext, TEST_SUB_ID, null, 0, mMockRcsStats);
mHandler = mTestGbaManager.getHandler();
try {
mLooper = new TestableLooper(mHandler.getLooper());
@@ -216,6 +221,45 @@
assertTrue(!mTestGbaManager.isServiceConnected());
}
+ @Test
+ @SmallTest
+ public void testMetricsGbaEvent() throws Exception {
+ mTestGbaManager.overrideServicePackage(TEST_DEFAULT_SERVICE_NAME.getPackageName());
+ mTestGbaManager.overrideReleaseTime(RELEASE_NEVER);
+
+ mLooper.processAllMessages();
+ bindAndConnectService(TEST_DEFAULT_SERVICE_NAME);
+ GbaAuthRequest request = createDefaultRequest();
+
+ // Failure case
+ mTestGbaManager.bootstrapAuthenticationRequest(request);
+ mLooper.processAllMessages();
+
+ ArgumentCaptor<GbaAuthRequest> captor = ArgumentCaptor.forClass(GbaAuthRequest.class);
+ verify(mMockGbaServiceBinder, times(1)).authenticationRequest(captor.capture());
+
+ GbaAuthRequest capturedRequest = captor.getValue();
+ IBootstrapAuthenticationCallback callback = capturedRequest.getCallback();
+ callback.onAuthenticationFailure(capturedRequest.getToken(),
+ GBA_EVENT__FAILED_REASON__FEATURE_NOT_READY);
+
+ verify(mMockRcsStats).onGbaFailureEvent(anyInt(),
+ eq(GBA_EVENT__FAILED_REASON__FEATURE_NOT_READY));
+
+ // Success case
+ mTestGbaManager.bootstrapAuthenticationRequest(request);
+ mLooper.processAllMessages();
+
+ ArgumentCaptor<GbaAuthRequest> captor2 = ArgumentCaptor.forClass(GbaAuthRequest.class);
+ verify(mMockGbaServiceBinder, times(2)).authenticationRequest(captor2.capture());
+
+ GbaAuthRequest capturedRequest2 = captor2.getValue();
+ IBootstrapAuthenticationCallback callback2 = capturedRequest2.getCallback();
+ callback2.onKeysAvailable(capturedRequest2.getToken(), "".getBytes(), "");
+
+ verify(mMockRcsStats).onGbaSuccessEvent(anyInt());
+ }
+
private ServiceConnection bindAndConnectService(ComponentName component) {
ServiceConnection connection = bindService(component);
IGbaService.Stub serviceStub = mock(IGbaService.Stub.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
index cbb3ba0..bdd35f1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkTypeControllerTest.java
@@ -919,6 +919,36 @@
}
@Test
+ public void testPrimaryTimerReset_theNetworkModeWithoutNr() throws Exception {
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ mBundle.putString(CarrierConfigManager.KEY_5G_ICON_DISPLAY_GRACE_PERIOD_STRING,
+ "connected_mmwave,any,10;connected,any,10;not_restricted_rrc_con,any,10");
+ broadcastCarrierConfigs();
+
+ assertEquals("connected", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA,
+ mNetworkTypeController.getOverrideNetworkType());
+
+ // remove NR from preferred network types
+ doReturn(RadioAccessFamily.getRafFromNetworkType(
+ TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA)).when(
+ mPhone).getCachedAllowedNetworkTypesBitmask();
+
+ // trigger 10 second timer after disconnecting from NR, and then it does the timer reset
+ // since the network mode without the NR capability.
+ doReturn(NetworkRegistrationInfo.NR_STATE_NONE).when(mServiceState).getNrState();
+ mNetworkTypeController.sendMessage(EVENT_NR_STATE_CHANGED);
+ processAllMessages();
+
+ // timer should be reset.
+ assertEquals("legacy", getCurrentState().getName());
+ assertEquals(TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE,
+ mNetworkTypeController.getOverrideNetworkType());
+ assertFalse(mNetworkTypeController.is5GHysteresisActive());
+ }
+
+ @Test
public void testPrimaryTimerExpireMmwave() throws Exception {
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/QosBearerSessionTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/QosBearerSessionTest.java
new file mode 100644
index 0000000..2e15d3b
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/QosBearerSessionTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.dataconnection;
+
+import static com.android.internal.telephony.dataconnection.QosCallbackTrackerTest.createEpsQos;
+import static com.android.internal.telephony.dataconnection.QosCallbackTrackerTest.createIpv4QosFilter;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.telephony.data.QosBearerFilter;
+import android.telephony.data.QosBearerSession;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+public class QosBearerSessionTest extends TelephonyTest {
+
+ @Test
+ public void testParcel() {
+ ArrayList<QosBearerFilter> qosFilters = new ArrayList<>();
+ qosFilters.add(createIpv4QosFilter("122.22.22.22",
+ new QosBearerFilter.PortRange(2222, 2222), 45));
+ QosBearerSession qosBearerSession = new QosBearerSession(1235,
+ createEpsQos(5, 6, 7, 8), qosFilters);
+
+ Parcel p = Parcel.obtain();
+ qosBearerSession.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ QosBearerSession qosBearerSession2 = QosBearerSession.CREATOR.createFromParcel(p);
+ assertThat(qosBearerSession).isEqualTo(qosBearerSession2);
+ }
+
+ @Test
+ public void testEquals() {
+ ArrayList<QosBearerFilter> qosFilters = new ArrayList<>();
+ qosFilters.add(createIpv4QosFilter("122.22.22.22",
+ new QosBearerFilter.PortRange(2222, 2222), 45));
+ QosBearerSession qosBearerSession = new QosBearerSession(1235,
+ createEpsQos(5, 6, 7, 8), qosFilters);
+
+ ArrayList<QosBearerFilter> qosFilters2 = new ArrayList<>();
+ qosFilters2.add(createIpv4QosFilter("122.22.22.22",
+ new QosBearerFilter.PortRange(2222, 2222), 45));
+ QosBearerSession qosBearerSession2 = new QosBearerSession(1235,
+ createEpsQos(5, 6, 7, 8), qosFilters2);
+
+ assertThat(qosBearerSession).isEqualTo(qosBearerSession2);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/QosCallbackTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/QosCallbackTrackerTest.java
index 5a8b540..62bf6e5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/QosCallbackTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/QosCallbackTrackerTest.java
@@ -16,13 +16,10 @@
package com.android.internal.telephony.dataconnection;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
@@ -32,31 +29,28 @@
import android.net.InetAddresses;
import android.net.LinkAddress;
import android.net.Network;
-import android.net.NetworkAgent;
import android.telephony.data.EpsBearerQosSessionAttributes;
import android.telephony.data.EpsQos;
import android.telephony.data.QosBearerFilter;
import android.telephony.data.QosBearerSession;
-
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import com.android.internal.telephony.Phone;
import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.metrics.RcsStats;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
-import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -94,9 +88,13 @@
}
@Mock
+ private Phone mPhone;
+ @Mock
private DcNetworkAgent mDcNetworkAgent;
@Mock
private Network mNetwork;
+ @Mock
+ private RcsStats mRcsStats;
private QosCallbackTracker mQosCallbackTracker;
@@ -105,7 +103,9 @@
super.setUp(getClass().getSimpleName());
doReturn(mNetwork).when(mDcNetworkAgent).getNetwork();
doReturn(100).when(mNetwork).getNetId();
- mQosCallbackTracker = new QosCallbackTracker(mDcNetworkAgent);
+ doReturn(0).when(mPhone).getPhoneId();
+ mQosCallbackTracker = new QosCallbackTracker(mDcNetworkAgent, mPhone.getPhoneId(),
+ mRcsStats);
processAllMessages();
}
@@ -114,7 +114,7 @@
super.tearDown();
}
- private EpsQos createEpsQos(int dlMbr, int ulMbr, int dlGbr, int ulGbr) {
+ public static EpsQos createEpsQos(int dlMbr, int ulMbr, int dlGbr, int ulGbr) {
// Build android.hardware.radio.V1_6.EpsQos
android.hardware.radio.V1_6.EpsQos halEpsQos = new android.hardware.radio.V1_6.EpsQos();
halEpsQos.qci = 4;
@@ -126,7 +126,7 @@
return new EpsQos(halEpsQos);
}
- private QosBearerFilter createIpv4QosFilter(String localAddress,
+ public static QosBearerFilter createIpv4QosFilter(String localAddress,
QosBearerFilter.PortRange localPort, int precedence) {
return new QosBearerFilter(
Arrays.asList(
@@ -135,7 +135,7 @@
7, 987, 678, QosBearerFilter.QOS_FILTER_DIRECTION_BIDIRECTIONAL, precedence);
}
- private QosBearerFilter createIpv4QosFilter(String localAddress, String remoteAddress,
+ public static QosBearerFilter createIpv4QosFilter(String localAddress, String remoteAddress,
QosBearerFilter.PortRange localPort, QosBearerFilter.PortRange remotePort,
int precedence) {
return new QosBearerFilter(
@@ -486,5 +486,42 @@
eq(1235), any(EpsBearerQosSessionAttributes.class));
}
+
+ @Test
+ @SmallTest
+ public void testQosMetrics() throws Exception {
+ final int callbackId = 1;
+ final int slotId = mPhone.getPhoneId();
+ // Add filter before update session
+ Filter filter1 = new Filter(new InetSocketAddress(
+ InetAddresses.parseNumericAddress("155.55.55.55"), 2222),
+ new InetSocketAddress(InetAddresses.parseNumericAddress("144.44.44.44"), 2223));
+
+ mQosCallbackTracker.addFilter(callbackId, filter1);
+ verify(mRcsStats, never()).onImsDedicatedBearerListenerAdded(eq(callbackId), eq(slotId),
+ anyInt(), anyInt());
+
+ // QosBearerFilter
+ ArrayList<QosBearerFilter> qosFilters1 = new ArrayList<>();
+ qosFilters1.add(createIpv4QosFilter("155.55.55.55", "144.44.44.44",
+ new QosBearerFilter.PortRange(2222, 2222),
+ new QosBearerFilter.PortRange(2223, 2223), 45));
+
+ ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
+ qosSessions.add(new QosBearerSession(1234, createEpsQos(5, 6, 7, 8), qosFilters1));
+ mQosCallbackTracker.updateSessions(qosSessions);
+
+ verify(mRcsStats, times(1))
+ .onImsDedicatedBearerListenerUpdateSession(
+ eq(callbackId), eq(slotId), anyInt(), anyInt(), eq(true));
+
+ mQosCallbackTracker.addFilter(callbackId, filter1);
+ verify(mRcsStats, times(1)).onImsDedicatedBearerListenerAdded(
+ anyInt(), anyInt(), anyInt(), anyInt());
+
+ mQosCallbackTracker.removeFilter(callbackId);
+ verify(mRcsStats, times(1))
+ .onImsDedicatedBearerListenerRemoved(callbackId);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
index f200a0f..a9b056f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -16,6 +16,12 @@
package com.android.internal.telephony.metrics;
+import static com.android.internal.telephony.TelephonyStatsLog.GBA_EVENT__FAILED_REASON__FEATURE_NOT_READY;
+import static com.android.internal.telephony.TelephonyStatsLog.GBA_EVENT__FAILED_REASON__UNKNOWN;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__ERROR;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__CLIENT_PARAMS_SENT;
+import static com.android.internal.telephony.TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__TRIGGER_RCS_RECONFIGURATION;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO;
@@ -41,16 +47,32 @@
import android.telephony.DisconnectCause;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
+import android.telephony.TelephonyProtoEnums;
import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.SipDelegateManager;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.internal.telephony.TelephonyStatsLog;
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.GbaEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerListenerEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationFeatureTagStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationServiceDescStats;
import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats;
import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms;
+import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.RcsClientProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipDelegateStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipMessageResponse;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportFeatureTagStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.UceEventStats;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.AudioCodec;
@@ -80,6 +102,10 @@
private static final int CARRIER1_ID = 1;
private static final int CARRIER2_ID = 1187;
private static final int CARRIER3_ID = 1435;
+ private static final int SLOT_ID1 = 1;
+ private static final int SLOT_ID2 = 2;
+ private static final int REGISTRATION1_TECH = 1;
+ private static final int REGISTRATION2_TECH = 2;
@Mock private FileOutputStream mTestFileOutputStream;
@@ -135,6 +161,64 @@
private DataCallSession mDataCallSession0;
private DataCallSession mDataCallSession1;
+ // RCS registration feature tags for slot 0 and 1
+ private ImsRegistrationFeatureTagStats mImsRegistrationFeatureTagStats1Proto;
+ private ImsRegistrationFeatureTagStats mImsRegistrationFeatureTagStats2Proto;
+ private ImsRegistrationFeatureTagStats[] mImsRegistrationFeatureTagStatses;
+
+ // RCS provisioning client stats for slot 0 and 1
+ private RcsClientProvisioningStats mRcsClientProvisioningStats1Proto;
+ private RcsClientProvisioningStats mRcsClientProvisioningStats2Proto;
+ private RcsClientProvisioningStats[] mRcsClientProvisioningStatses;
+
+ // RCS provisioning ACS stats for slot 0 and 1
+ private RcsAcsProvisioningStats mRcsAcsProvisioningStats1Proto;
+ private RcsAcsProvisioningStats mRcsAcsProvisioningStats2Proto;
+ private RcsAcsProvisioningStats[] mRcsAcsProvisioningStatses;
+
+ private ImsRegistrationServiceDescStats mImsRegistrationServiceIm;
+ private ImsRegistrationServiceDescStats mImsRegistrationServiceFt;
+ private ImsRegistrationServiceDescStats[] mImsRegistrationServiceDescStats;
+
+ // IMS dedicated bearer listener event stats for slot 0 and 1
+ private ImsDedicatedBearerListenerEvent mImsDedicatedBearerListenerEvent1;
+ private ImsDedicatedBearerListenerEvent mImsDedicatedBearerListenerEvent2;
+ private ImsDedicatedBearerListenerEvent[] mImsDedicatedBearerListenerEvents;
+
+ // IMS dedicated bearer event stats for slot 0 and 1
+ private ImsDedicatedBearerEvent mImsDedicatedBearerEvent1;
+ private ImsDedicatedBearerEvent mImsDedicatedBearerEvent2;
+ private ImsDedicatedBearerEvent[] mImsDedicatedBearerEvents;
+
+ private UceEventStats mUceEventStats1;
+ private UceEventStats mUceEventStats2;
+ private UceEventStats[] mUceEventStatses;
+
+ private PresenceNotifyEvent mPresenceNotifyEvent1;
+ private PresenceNotifyEvent mPresenceNotifyEvent2;
+ private PresenceNotifyEvent[] mPresenceNotifyEvents;
+
+ private SipTransportFeatureTagStats mSipTransportFeatureTagStats1;
+ private SipTransportFeatureTagStats mSipTransportFeatureTagStats2;
+ private SipTransportFeatureTagStats[] mSipTransportFeatureTagStatsArray;
+
+ private SipDelegateStats mSipDelegateStats1;
+ private SipDelegateStats mSipDelegateStats2;
+ private SipDelegateStats mSipDelegateStats3;
+ private SipDelegateStats[] mSipDelegateStatsArray;
+
+ private GbaEvent mGbaEvent1;
+ private GbaEvent mGbaEvent2;
+ private GbaEvent[] mGbaEvent;
+
+ private SipMessageResponse mSipMessageResponse1;
+ private SipMessageResponse mSipMessageResponse2;
+ private SipMessageResponse[] mSipMessageResponse;
+
+ private SipTransportSession mSipTransportSession1;
+ private SipTransportSession mSipTransportSession2;
+ private SipTransportSession[] mSipTransportSession;
+
private void makeTestData() {
// MO call with SRVCC (LTE to UMTS)
mCall1Proto = new VoiceCallSession();
@@ -455,6 +539,309 @@
mDataCallSession1.setupFailed = false;
mDataCallSession1.durationMinutes = 5;
mDataCallSession1.ongoing = false;
+
+ // RCS registrtion feature tag slot 0
+ mImsRegistrationFeatureTagStats1Proto = new ImsRegistrationFeatureTagStats();
+ mImsRegistrationFeatureTagStats1Proto.carrierId = CARRIER1_ID;
+ mImsRegistrationFeatureTagStats1Proto.slotId = 0;
+ mImsRegistrationFeatureTagStats1Proto.featureTagName = 1;
+ mImsRegistrationFeatureTagStats1Proto.registrationTech = TelephonyManager.NETWORK_TYPE_LTE;
+ mImsRegistrationFeatureTagStats1Proto.registeredMillis = 3600L;
+
+ // RCS registrtion feature tag slot 1
+ mImsRegistrationFeatureTagStats2Proto = new ImsRegistrationFeatureTagStats();
+ mImsRegistrationFeatureTagStats2Proto.carrierId = CARRIER2_ID;
+ mImsRegistrationFeatureTagStats2Proto.slotId = 1;
+ mImsRegistrationFeatureTagStats2Proto.featureTagName = 0;
+ mImsRegistrationFeatureTagStats2Proto.registrationTech = TelephonyManager.NETWORK_TYPE_LTE;
+ mImsRegistrationFeatureTagStats2Proto.registeredMillis = 3600L;
+
+ mImsRegistrationFeatureTagStatses =
+ new ImsRegistrationFeatureTagStats[] {
+ mImsRegistrationFeatureTagStats1Proto,
+ mImsRegistrationFeatureTagStats2Proto
+ };
+
+ // RCS client provisioning stats slot 0
+ mRcsClientProvisioningStats1Proto = new RcsClientProvisioningStats();
+ mRcsClientProvisioningStats1Proto.carrierId = CARRIER1_ID;
+ mRcsClientProvisioningStats1Proto.slotId = 0;
+ mRcsClientProvisioningStats1Proto.event =
+ RCS_CLIENT_PROVISIONING_STATS__EVENT__CLIENT_PARAMS_SENT;
+ mRcsClientProvisioningStats1Proto.count = 1;
+
+ // RCS client provisioning stats slot 1
+ mRcsClientProvisioningStats2Proto = new RcsClientProvisioningStats();
+ mRcsClientProvisioningStats2Proto.carrierId = CARRIER2_ID;
+ mRcsClientProvisioningStats2Proto.slotId = 1;
+ mRcsClientProvisioningStats2Proto.event =
+ RCS_CLIENT_PROVISIONING_STATS__EVENT__TRIGGER_RCS_RECONFIGURATION;
+ mRcsClientProvisioningStats2Proto.count = 1;
+
+ mRcsClientProvisioningStatses =
+ new RcsClientProvisioningStats[] {
+ mRcsClientProvisioningStats1Proto,
+ mRcsClientProvisioningStats2Proto
+ };
+
+ // RCS ACS provisioning stats : error response
+ mRcsAcsProvisioningStats1Proto = new RcsAcsProvisioningStats();
+ mRcsAcsProvisioningStats1Proto.carrierId = CARRIER1_ID;
+ mRcsAcsProvisioningStats1Proto.slotId = 0;
+ mRcsAcsProvisioningStats1Proto.responseCode = 401;
+ mRcsAcsProvisioningStats1Proto.responseType =
+ RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__ERROR;
+ mRcsAcsProvisioningStats1Proto.isSingleRegistrationEnabled = true;
+ mRcsAcsProvisioningStats1Proto.count = 1;
+ mRcsAcsProvisioningStats1Proto.stateTimerMillis = START_TIME_MILLIS;
+
+ // RCS ACS provisioning stats : xml
+ mRcsAcsProvisioningStats2Proto = new RcsAcsProvisioningStats();
+ mRcsAcsProvisioningStats2Proto.carrierId = CARRIER1_ID;
+ mRcsAcsProvisioningStats2Proto.slotId = 0;
+ mRcsAcsProvisioningStats2Proto.responseCode = 200;
+ mRcsAcsProvisioningStats2Proto.responseType =
+ RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML;
+ mRcsAcsProvisioningStats2Proto.isSingleRegistrationEnabled = true;
+ mRcsAcsProvisioningStats2Proto.count = 1;
+ mRcsAcsProvisioningStats2Proto.stateTimerMillis = START_TIME_MILLIS;
+
+ mRcsAcsProvisioningStatses =
+ new RcsAcsProvisioningStats[] {
+ mRcsAcsProvisioningStats1Proto,
+ mRcsAcsProvisioningStats2Proto
+ };
+
+ mImsRegistrationServiceIm = new ImsRegistrationServiceDescStats();
+ mImsRegistrationServiceIm.carrierId = CARRIER1_ID;
+ mImsRegistrationServiceIm.slotId = SLOT_ID1;
+ mImsRegistrationServiceIm.serviceIdName = 0;
+ mImsRegistrationServiceIm.serviceIdVersion = 1.0f;
+ mImsRegistrationServiceIm.registrationTech = REGISTRATION1_TECH;
+ mImsRegistrationServiceIm.publishedMillis = START_TIME_MILLIS;
+
+ mImsRegistrationServiceFt = new ImsRegistrationServiceDescStats();
+ mImsRegistrationServiceFt.carrierId = CARRIER2_ID;
+ mImsRegistrationServiceFt.slotId = SLOT_ID2;
+ mImsRegistrationServiceFt.serviceIdName = 1;
+ mImsRegistrationServiceFt.serviceIdVersion = 2.0f;
+ mImsRegistrationServiceFt.registrationTech = REGISTRATION2_TECH;
+ mImsRegistrationServiceIm.publishedMillis = START_TIME_MILLIS;
+
+ mImsRegistrationServiceDescStats =
+ new ImsRegistrationServiceDescStats[] {
+ mImsRegistrationServiceIm, mImsRegistrationServiceFt
+ };
+
+
+ mImsDedicatedBearerListenerEvent1 = new ImsDedicatedBearerListenerEvent();
+ mImsDedicatedBearerListenerEvent1.carrierId = CARRIER1_ID;
+ mImsDedicatedBearerListenerEvent1.slotId = SLOT_ID1;
+ mImsDedicatedBearerListenerEvent1.ratAtEnd = TelephonyManager.NETWORK_TYPE_LTE;
+ mImsDedicatedBearerListenerEvent1.qci = 5;
+ mImsDedicatedBearerListenerEvent1.dedicatedBearerEstablished = true;
+ mImsDedicatedBearerListenerEvent1.eventCount = 1;
+
+ mImsDedicatedBearerListenerEvent2 = new ImsDedicatedBearerListenerEvent();
+ mImsDedicatedBearerListenerEvent2.carrierId = CARRIER2_ID;
+ mImsDedicatedBearerListenerEvent2.slotId = SLOT_ID1;
+ mImsDedicatedBearerListenerEvent2.ratAtEnd = TelephonyManager.NETWORK_TYPE_NR;
+ mImsDedicatedBearerListenerEvent2.qci = 6;
+ mImsDedicatedBearerListenerEvent2.dedicatedBearerEstablished = true;
+ mImsDedicatedBearerListenerEvent2.eventCount = 1;
+
+ mImsDedicatedBearerListenerEvents =
+ new ImsDedicatedBearerListenerEvent[] {
+ mImsDedicatedBearerListenerEvent1, mImsDedicatedBearerListenerEvent2
+ };
+
+
+ mImsDedicatedBearerEvent1 = new ImsDedicatedBearerEvent();
+ mImsDedicatedBearerEvent1.carrierId = CARRIER1_ID;
+ mImsDedicatedBearerEvent1.slotId = SLOT_ID1;
+ mImsDedicatedBearerEvent1.ratAtEnd = TelephonyManager.NETWORK_TYPE_LTE;
+ mImsDedicatedBearerEvent1.qci = 5;
+ mImsDedicatedBearerEvent1.bearerState =
+ TelephonyStatsLog.IMS_DEDICATED_BEARER_EVENT__BEARER_STATE__STATE_ADDED;
+ mImsDedicatedBearerEvent1.localConnectionInfoReceived = true;
+ mImsDedicatedBearerEvent1.remoteConnectionInfoReceived = true;
+ mImsDedicatedBearerEvent1.hasListeners = true;
+ mImsDedicatedBearerEvent1.count = 1;
+
+ mImsDedicatedBearerEvent2 = new ImsDedicatedBearerEvent();
+ mImsDedicatedBearerEvent2.carrierId = CARRIER1_ID;
+ mImsDedicatedBearerEvent2.slotId = SLOT_ID1;
+ mImsDedicatedBearerEvent2.ratAtEnd = TelephonyManager.NETWORK_TYPE_NR;
+ mImsDedicatedBearerEvent2.qci = 6;
+ mImsDedicatedBearerEvent2.bearerState =
+ TelephonyStatsLog.IMS_DEDICATED_BEARER_EVENT__BEARER_STATE__STATE_MODIFIED;
+ mImsDedicatedBearerEvent2.localConnectionInfoReceived = true;
+ mImsDedicatedBearerEvent2.remoteConnectionInfoReceived = true;
+ mImsDedicatedBearerEvent2.hasListeners = true;
+ mImsDedicatedBearerEvent2.count = 1;
+
+ mImsDedicatedBearerEvents =
+ new ImsDedicatedBearerEvent[] {
+ mImsDedicatedBearerEvent1, mImsDedicatedBearerEvent2
+ };
+
+
+ mUceEventStats1 = new UceEventStats();
+ mUceEventStats1.carrierId = CARRIER1_ID;
+ mUceEventStats1.slotId = SLOT_ID1;
+ mUceEventStats1.type = 1;
+ mUceEventStats1.successful = true;
+ mUceEventStats1.commandCode = 0;
+ mUceEventStats1.networkResponse = 200;
+ mUceEventStats1.count = 1;
+
+ mUceEventStats2 = new UceEventStats();
+ mUceEventStats2.carrierId = CARRIER2_ID;
+ mUceEventStats2.slotId = SLOT_ID2;
+ mUceEventStats2.type = 2;
+ mUceEventStats2.successful = false;
+ mUceEventStats2.commandCode = 2;
+ mUceEventStats2.networkResponse = 0;
+ mUceEventStats2.count = 1;
+ mUceEventStatses = new UceEventStats[] {mUceEventStats1, mUceEventStats2};
+
+ mPresenceNotifyEvent1 = new PresenceNotifyEvent();
+ mPresenceNotifyEvent1.carrierId = CARRIER1_ID;
+ mPresenceNotifyEvent1.slotId = SLOT_ID1;
+ mPresenceNotifyEvent1.reason = 1;
+ mPresenceNotifyEvent1.contentBodyReceived = true;
+ mPresenceNotifyEvent1.rcsCapsCount = 1;
+ mPresenceNotifyEvent1.mmtelCapsCount = 1;
+ mPresenceNotifyEvent1.noCapsCount = 0;
+ mPresenceNotifyEvent1.count = 1;
+
+ mPresenceNotifyEvent2 = new PresenceNotifyEvent();
+ mPresenceNotifyEvent2.carrierId = CARRIER2_ID;
+ mPresenceNotifyEvent2.slotId = SLOT_ID2;
+ mPresenceNotifyEvent2.reason = 1;
+ mPresenceNotifyEvent2.contentBodyReceived = false;
+ mPresenceNotifyEvent2.rcsCapsCount = 0;
+ mPresenceNotifyEvent2.mmtelCapsCount = 0;
+ mPresenceNotifyEvent2.noCapsCount = 1;
+ mPresenceNotifyEvent2.count = 1;
+ mPresenceNotifyEvents = new PresenceNotifyEvent[] {mPresenceNotifyEvent1,
+ mPresenceNotifyEvent2};
+
+ //A destroyed SipDelegate
+ mSipDelegateStats1 = new SipDelegateStats();
+ mSipDelegateStats1.carrierId = CARRIER1_ID;
+ mSipDelegateStats1.slotId = SLOT_ID1;
+ mSipDelegateStats1.uptimeMillis = 1000L;
+ mSipDelegateStats1.destroyReason =
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP;
+
+ //An active SipDelegate
+ mSipDelegateStats2 = new SipDelegateStats();
+ mSipDelegateStats2.carrierId = CARRIER1_ID;
+ mSipDelegateStats2.slotId = SLOT_ID1;
+ mSipDelegateStats2.uptimeMillis = 1000L;
+ mSipDelegateStats2.destroyReason =
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD;
+
+ //An active SipDelegate
+ mSipDelegateStats3 = new SipDelegateStats();
+ mSipDelegateStats3.carrierId = CARRIER2_ID;
+ mSipDelegateStats3.slotId = SLOT_ID2;
+ mSipDelegateStats3.uptimeMillis = 3000L;
+ mSipDelegateStats3.destroyReason =
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN;
+
+ //A registered SipTransportFeatureTag
+ mSipTransportFeatureTagStats1 = new SipTransportFeatureTagStats();
+ mSipTransportFeatureTagStats1.carrierId = CARRIER1_ID;
+ mSipTransportFeatureTagStats1.slotId = SLOT_ID1;
+ mSipTransportFeatureTagStats1.featureTagName = TelephonyProtoEnums.IMS_FEATURE_TAG_CHAT_IM;
+ mSipTransportFeatureTagStats1.sipTransportDeniedReason = RcsStats.NONE;
+ mSipTransportFeatureTagStats1.sipTransportDeregisteredReason = RcsStats.NONE;
+ mSipTransportFeatureTagStats1.associatedMillis = 1000L;
+
+ //A denied SipTransportFeatureTag
+ mSipTransportFeatureTagStats2 = new SipTransportFeatureTagStats();
+ mSipTransportFeatureTagStats2.carrierId = CARRIER1_ID;
+ mSipTransportFeatureTagStats2.slotId = SLOT_ID1;
+ mSipTransportFeatureTagStats2.featureTagName = 1;
+ mSipTransportFeatureTagStats2.sipTransportDeniedReason =
+ SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE;
+ mSipTransportFeatureTagStats2.sipTransportDeregisteredReason = RcsStats.NONE;
+ mSipTransportFeatureTagStats2.associatedMillis = 1000L;
+
+ mSipDelegateStatsArray = new SipDelegateStats[]{mSipDelegateStats2, mSipDelegateStats3};
+
+ mSipTransportFeatureTagStatsArray = new SipTransportFeatureTagStats[]
+ {mSipTransportFeatureTagStats1, mSipTransportFeatureTagStats2};
+
+ mGbaEvent1 = new GbaEvent();
+ mGbaEvent1.carrierId = CARRIER1_ID;
+ mGbaEvent1.slotId = SLOT_ID1;
+ mGbaEvent1.successful = true;
+ mGbaEvent1.failedReason = GBA_EVENT__FAILED_REASON__UNKNOWN;
+ mGbaEvent1.count = 1;
+
+ mGbaEvent2 = new GbaEvent();
+ mGbaEvent2.carrierId = CARRIER2_ID;
+ mGbaEvent2.slotId = SLOT_ID2;
+ mGbaEvent2.successful = false;
+ mGbaEvent2.failedReason = GBA_EVENT__FAILED_REASON__FEATURE_NOT_READY;
+ mGbaEvent2.count = 1;
+
+ mGbaEvent = new GbaEvent[] {mGbaEvent1, mGbaEvent2};
+
+ //stats slot 0
+ mSipMessageResponse1 = new SipMessageResponse();
+ mSipMessageResponse1.carrierId = CARRIER1_ID;
+ mSipMessageResponse1.slotId = SLOT_ID1;
+ //"INVITE"
+ mSipMessageResponse1.sipMessageMethod = 2;
+ mSipMessageResponse1.sipMessageResponse = 200;
+ mSipMessageResponse1.sipMessageDirection = 1;
+ mSipMessageResponse1.messageError = 0;
+ mSipMessageResponse1.count = 1;
+
+ //stats slot 1
+ mSipMessageResponse2 = new SipMessageResponse();
+ mSipMessageResponse2.carrierId = CARRIER2_ID;
+ mSipMessageResponse2.slotId = SLOT_ID2;
+ //"INVITE"
+ mSipMessageResponse2.sipMessageMethod = 2;
+ mSipMessageResponse2.sipMessageResponse = 200;
+ mSipMessageResponse2.sipMessageDirection = 0;
+ mSipMessageResponse2.messageError = 0;
+ mSipMessageResponse2.count = 1;
+
+ mSipMessageResponse =
+ new SipMessageResponse[] {mSipMessageResponse1, mSipMessageResponse2};
+
+ // stats slot 0
+ mSipTransportSession1 = new SipTransportSession();
+ mSipTransportSession1.carrierId = CARRIER1_ID;
+ mSipTransportSession1.slotId = SLOT_ID1;
+ //"INVITE"
+ mSipTransportSession1.sessionMethod = 2;
+ mSipTransportSession1.sipMessageDirection = 1;
+ mSipTransportSession1.sipResponse = 200;
+ mSipTransportSession1.sessionCount = 1;
+ mSipTransportSession1.endedGracefullyCount = 1;
+ mSipTransportSession1.isEndedGracefully = true;
+
+ // stats slot 1
+ mSipTransportSession2 = new SipTransportSession();
+ mSipTransportSession2.carrierId = CARRIER2_ID;
+ mSipTransportSession2.slotId = SLOT_ID2;
+ //"INVITE"
+ mSipTransportSession2.sessionMethod = 2;
+ mSipTransportSession2.sipMessageDirection = 0;
+ mSipTransportSession2.sipResponse = 200;
+ mSipTransportSession2.sessionCount = 1;
+ mSipTransportSession2.endedGracefullyCount = 1;
+ mSipTransportSession2.isEndedGracefully = true;
+
+ mSipTransportSession =
+ new SipTransportSession[] {mSipTransportSession1, mSipTransportSession2};
}
private static class TestablePersistAtomsStorage extends PersistAtomsStorage {
@@ -1311,12 +1698,32 @@
verifyCurrentStateSavedToFileOnce();
DataCallSession[] dataCalls = mPersistAtomsStorage.getDataCallSessions(0L);
assertProtoArrayEqualsIgnoringOrder(
- new DataCallSession[] {mDataCallSession0, mDataCallSession1},
+ new DataCallSession[]{mDataCallSession0, mDataCallSession1},
dataCalls);
}
@Test
@SmallTest
+ public void addImsRegistrationFeatureTagStats_emptyProto() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage
+ .addImsRegistrationFeatureTagStats(mImsRegistrationFeatureTagStats1Proto);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ verifyCurrentStateSavedToFileOnce();
+
+ ImsRegistrationFeatureTagStats[] expected =
+ new ImsRegistrationFeatureTagStats[] {
+ mImsRegistrationFeatureTagStats1Proto
+ };
+ assertProtoArrayEquals(
+ expected, mPersistAtomsStorage.getImsRegistrationFeatureTagStats(0L));
+ }
+
+ @Test
+ @SmallTest
public void addDataCallSession_existingEntry()
throws Exception {
createEmptyTestFile();
@@ -1340,7 +1747,1227 @@
verifyCurrentStateSavedToFileOnce();
DataCallSession[] dataCalls = mPersistAtomsStorage.getDataCallSessions(0L);
assertProtoArrayEqualsIgnoringOrder(
- new DataCallSession[] {totalDataCallSession0}, dataCalls);
+ new DataCallSession[]{totalDataCallSession0}, dataCalls);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationFeatureTagStats_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage
+ .addImsRegistrationFeatureTagStats(mImsRegistrationFeatureTagStats1Proto);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ mPersistAtomsStorage
+ .addImsRegistrationFeatureTagStats(mImsRegistrationFeatureTagStats2Proto);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ verifyCurrentStateSavedToFileOnce();
+
+ ImsRegistrationFeatureTagStats[] expected =
+ new ImsRegistrationFeatureTagStats[] {
+ mImsRegistrationFeatureTagStats1Proto,
+ mImsRegistrationFeatureTagStats2Proto
+ };
+ // 2 atoms stored on initially and when try to add 2 same atoms, should be increased.
+ assertProtoArrayEqualsIgnoringOrder(
+ expected, mPersistAtomsStorage.getImsRegistrationFeatureTagStats(0L));
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationFeatureTagStats_tooManyEntries() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ int maxCount = 10;
+ for (int i = 0; i < maxCount; i++) {
+ mPersistAtomsStorage
+ .addImsRegistrationFeatureTagStats(
+ copyOf(mImsRegistrationFeatureTagStats1Proto));
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+
+ mPersistAtomsStorage
+ .addImsRegistrationFeatureTagStats(copyOf(mImsRegistrationFeatureTagStats2Proto));
+
+ verifyCurrentStateSavedToFileOnce();
+
+ ImsRegistrationFeatureTagStats[] result =
+ mPersistAtomsStorage.getImsRegistrationFeatureTagStats(0L);
+
+ // tried store 26 statses, but only 2 statses stored
+ // total time 3600L * maxCount
+ assertHasStatsCountTime(result, mImsRegistrationFeatureTagStats1Proto, 1,
+ maxCount * 3600L);
+ // total time 3600L * 1
+ assertHasStatsCountTime(result, mImsRegistrationFeatureTagStats2Proto, 1,
+ 1 * 3600L);
+ }
+
+ @Test
+ @SmallTest
+ public void getImsRegistrationFeatureTagStats_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ ImsRegistrationFeatureTagStats[] result =
+ mPersistAtomsStorage.getImsRegistrationFeatureTagStats(100L);
+
+ // should be denied
+ assertNull(result);
+ }
+
+ @Test
+ @SmallTest
+ public void getImsRegistrationFeatureTagStats_withSavedAtoms() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ ImsRegistrationFeatureTagStats[] statses1 =
+ mPersistAtomsStorage.getImsRegistrationFeatureTagStats(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ ImsRegistrationFeatureTagStats[] statses2 =
+ mPersistAtomsStorage.getImsRegistrationFeatureTagStats(50L);
+
+ // first results of get should have two atoms, second should be empty
+ // pull timestamp should be updated and saved
+ assertProtoArrayEqualsIgnoringOrder(mImsRegistrationFeatureTagStatses, statses1);
+ assertProtoArrayEquals(new ImsRegistrationFeatureTagStats[0], statses2);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ mPersistAtomsStorage.getAtomsProto()
+ .imsRegistrationFeatureTagStatsPullTimestampMillis);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(
+ START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).imsRegistrationFeatureTagStatsPullTimestampMillis);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).imsRegistrationFeatureTagStatsPullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void addRcsClientProvisioningStats_emptyProto() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage
+ .addRcsClientProvisioningStats(mRcsClientProvisioningStats1Proto);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ mPersistAtomsStorage
+ .addRcsClientProvisioningStats(mRcsClientProvisioningStats2Proto);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ verifyCurrentStateSavedToFileOnce();
+
+ RcsClientProvisioningStats[] expected =
+ new RcsClientProvisioningStats[] {
+ mRcsClientProvisioningStats1Proto,
+ mRcsClientProvisioningStats2Proto
+ };
+
+ assertProtoArrayEqualsIgnoringOrder(
+ expected, mPersistAtomsStorage.getRcsClientProvisioningStats(0L));
+ }
+
+ @Test
+ @SmallTest
+ public void addRcsClientProvisioningStats_tooManyEntries() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ // store 11 same atoms, but only 1 atoms stored with count 11
+ for (int i = 0; i < 11; i++) {
+ mPersistAtomsStorage
+ .addRcsClientProvisioningStats(mRcsClientProvisioningStats1Proto);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+ // store 1 different atom and count 1
+ mPersistAtomsStorage
+ .addRcsClientProvisioningStats(mRcsClientProvisioningStats2Proto);
+
+ verifyCurrentStateSavedToFileOnce();
+
+ RcsClientProvisioningStats[] result =
+ mPersistAtomsStorage.getRcsClientProvisioningStats(0L);
+
+ // first atom has count 11, the other has 1
+ assertHasStatsAndCount(result, mRcsClientProvisioningStats1Proto, 11);
+ assertHasStatsAndCount(result, mRcsClientProvisioningStats2Proto, 1);
+ }
+
+ @Test
+ @SmallTest
+ public void getRcsClientProvisioningStats_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ RcsClientProvisioningStats[] result =
+ mPersistAtomsStorage.getRcsClientProvisioningStats(100L);
+
+ // should be denied
+ assertNull(result);
+ }
+
+ @Test
+ @SmallTest
+ public void getRcsClientProvisioningStats_withSavedAtoms() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ RcsClientProvisioningStats[] statses1 =
+ mPersistAtomsStorage.getRcsClientProvisioningStats(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ RcsClientProvisioningStats[] statses2 =
+ mPersistAtomsStorage.getRcsClientProvisioningStats(50L);
+
+ // first results of get should have two atoms, second should be empty
+ // pull timestamp should be updated and saved
+ assertProtoArrayEqualsIgnoringOrder(mRcsClientProvisioningStatses, statses1);
+ assertProtoArrayEquals(new RcsClientProvisioningStats[0], statses2);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ mPersistAtomsStorage.getAtomsProto()
+ .rcsClientProvisioningStatsPullTimestampMillis);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(
+ START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).rcsClientProvisioningStatsPullTimestampMillis);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).rcsClientProvisioningStatsPullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void addRcsAcsProvisioningStats_emptyProto() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage
+ .addRcsAcsProvisioningStats(mRcsAcsProvisioningStats1Proto);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ mPersistAtomsStorage
+ .addRcsAcsProvisioningStats(mRcsAcsProvisioningStats2Proto);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ verifyCurrentStateSavedToFileOnce();
+
+ RcsAcsProvisioningStats[] expected =
+ new RcsAcsProvisioningStats[] {
+ mRcsAcsProvisioningStats1Proto,
+ mRcsAcsProvisioningStats2Proto
+ };
+
+ assertProtoArrayEqualsIgnoringOrder(
+ expected, mPersistAtomsStorage.getRcsAcsProvisioningStats(0L));
+ }
+
+ @Test
+ @SmallTest
+ public void addRcsAcsProvisioningStats_updateExistingEntries() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ // store 5 same atoms (1Proto), but only 1 atoms stored with count 5, total time 2000L * 5
+ // store 5 same atoms (2Proto), but only 1 atoms stored with count 5, total time 2000L * 5
+ for (int i = 0; i < 5; i++) {
+ mPersistAtomsStorage
+ .addRcsAcsProvisioningStats(copyOf(mRcsAcsProvisioningStats1Proto));
+ mPersistAtomsStorage.incTimeMillis(100L);
+ mPersistAtomsStorage
+ .addRcsAcsProvisioningStats(copyOf(mRcsAcsProvisioningStats2Proto));
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+ // add one more atoms (2Proto), count 6, total time 2000L * 6
+ mPersistAtomsStorage
+ .addRcsAcsProvisioningStats(copyOf(mRcsAcsProvisioningStats2Proto));
+
+ verifyCurrentStateSavedToFileOnce();
+
+ RcsAcsProvisioningStats[] result =
+ mPersistAtomsStorage.getRcsAcsProvisioningStats(0L);
+
+ // atom (1Proto) : count = 5, time = 2000L * 5
+ assertHasStatsAndCountDuration(result, mRcsAcsProvisioningStats1Proto, 5, 2000L * 5);
+ // atom (2Proto) : count = 6, time = 2000L * 6
+ assertHasStatsAndCountDuration(result, mRcsAcsProvisioningStats2Proto, 6, 2000L * 6);
+ }
+
+ @Test
+ @SmallTest
+ public void getRcsAcsProvisioningStats_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ RcsAcsProvisioningStats[] result =
+ mPersistAtomsStorage.getRcsAcsProvisioningStats(100L);
+
+ // should be denied
+ assertNull(result);
+ }
+
+ @Test
+ @SmallTest
+ public void getRcsAcstProvisioningStats_withSavedAtoms() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ RcsAcsProvisioningStats[] statses1 =
+ mPersistAtomsStorage.getRcsAcsProvisioningStats(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ RcsAcsProvisioningStats[] statses2 =
+ mPersistAtomsStorage.getRcsAcsProvisioningStats(50L);
+
+ // first results of get should have two atoms, second should be empty
+ // pull timestamp should be updated and saved
+ assertProtoArrayEqualsIgnoringOrder(mRcsAcsProvisioningStatses, statses1);
+ assertProtoArrayEquals(new RcsAcsProvisioningStats[0], statses2);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ mPersistAtomsStorage.getAtomsProto()
+ .rcsAcsProvisioningStatsPullTimestampMillis);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(
+ START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).rcsAcsProvisioningStatsPullTimestampMillis);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).rcsAcsProvisioningStatsPullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationServiceDescStats_emptyProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addImsRegistrationServiceDescStats(mImsRegistrationServiceIm);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationServiceDescStats[] outputs =
+ mPersistAtomsStorage.getImsRegistrationServiceDescStats(0L);
+ assertProtoArrayEquals(
+ new ImsRegistrationServiceDescStats[] {mImsRegistrationServiceIm}, outputs);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationServiceDescStats_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addImsRegistrationServiceDescStats(mImsRegistrationServiceIm);
+
+ mPersistAtomsStorage.addImsRegistrationServiceDescStats(mImsRegistrationServiceFt);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationServiceDescStats[] output =
+ mPersistAtomsStorage.getImsRegistrationServiceDescStats(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationServiceDescStats[] {
+ mImsRegistrationServiceIm,
+ mImsRegistrationServiceFt
+ },
+ output);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationServiceDescStats_tooManyServiceDesc() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ Queue<ImsRegistrationServiceDescStats> expectedOutput = new LinkedList<>();
+ // Add 101 registration terminations
+ for (int i = 0; i < 26 + 1; i++) {
+ ImsRegistrationServiceDescStats stats = copyOf(mImsRegistrationServiceIm);
+ stats.registrationTech = i;
+ expectedOutput.add(stats);
+ mPersistAtomsStorage.addImsRegistrationServiceDescStats(stats);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // The least recent (the first) registration termination should be evicted
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationServiceDescStats[] output =
+ mPersistAtomsStorage.getImsRegistrationServiceDescStats(0L);
+ expectedOutput.remove();
+ assertEquals(expectedOutput.size() - 1, output.length);
+ }
+
+ @Test
+ @SmallTest
+ public void getImsRegistrationServiceDescStats_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ ImsRegistrationServiceDescStats[] output =
+ mPersistAtomsStorage.getImsRegistrationServiceDescStats(100L);
+
+ // should be denied
+ assertNull(output);
+ }
+
+ @Test
+ @SmallTest
+ public void getImsRegistrationServiceDescStats_withSavedAtoms() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ ImsRegistrationServiceDescStats[] output1 =
+ mPersistAtomsStorage.getImsRegistrationServiceDescStats(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ ImsRegistrationServiceDescStats[] output2 =
+ mPersistAtomsStorage.getImsRegistrationServiceDescStats(50L);
+
+ // first set of results should equal to file contents, second should be empty, corresponding
+ // pull timestamp should be updated and saved
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationServiceDescStats[] {
+ mImsRegistrationServiceIm,
+ mImsRegistrationServiceFt
+ },
+ output1);
+ assertProtoArrayEquals(new ImsRegistrationServiceDescStats[0], output2);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ mPersistAtomsStorage.getAtomsProto()
+ .imsRegistrationServiceDescStatsPullTimestampMillis);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(
+ START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).imsRegistrationServiceDescStatsPullTimestampMillis);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).imsRegistrationServiceDescStatsPullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void addImsDedicatedBearerListenerEvent_emptyProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addImsDedicatedBearerListenerEvent(mImsDedicatedBearerListenerEvent1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ ImsDedicatedBearerListenerEvent[] outputs =
+ mPersistAtomsStorage.getImsDedicatedBearerListenerEvent(0L);
+ assertProtoArrayEquals(
+ new ImsDedicatedBearerListenerEvent[] {mImsDedicatedBearerListenerEvent1}, outputs);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsDedicatedBearerListenerEvent_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addImsDedicatedBearerListenerEvent(mImsDedicatedBearerListenerEvent1);
+
+ mPersistAtomsStorage.addImsDedicatedBearerListenerEvent(mImsDedicatedBearerListenerEvent2);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ ImsDedicatedBearerListenerEvent[] output =
+ mPersistAtomsStorage.getImsDedicatedBearerListenerEvent(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsDedicatedBearerListenerEvent[] {
+ mImsDedicatedBearerListenerEvent1, mImsDedicatedBearerListenerEvent2}, output);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsDedicatedBearerListenerEvent_withSameProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ mPersistAtomsStorage.addImsDedicatedBearerListenerEvent(mImsDedicatedBearerListenerEvent1);
+ mPersistAtomsStorage.addImsDedicatedBearerListenerEvent(mImsDedicatedBearerListenerEvent1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // The least recent (the first) registration termination should be evicted
+ verifyCurrentStateSavedToFileOnce();
+ ImsDedicatedBearerListenerEvent[] output =
+ mPersistAtomsStorage.getImsDedicatedBearerListenerEvent(0L);
+ assertEquals(mImsDedicatedBearerListenerEvent1.carrierId, output[0].carrierId);
+ assertEquals(mImsDedicatedBearerListenerEvent1.slotId, output[0].slotId);
+ assertEquals(mImsDedicatedBearerListenerEvent1.ratAtEnd, output[0].ratAtEnd);
+ assertEquals(mImsDedicatedBearerListenerEvent1.qci, output[0].qci);
+ assertEquals(mImsDedicatedBearerListenerEvent1.dedicatedBearerEstablished,
+ output[0].dedicatedBearerEstablished);
+ assertEquals(mImsDedicatedBearerListenerEvent1.eventCount,
+ output[0].eventCount);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsDedicatedBearerEvent_emptyProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addImsDedicatedBearerEvent(mImsDedicatedBearerEvent1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ ImsDedicatedBearerEvent[] outputs =
+ mPersistAtomsStorage.getImsDedicatedBearerEvent(0L);
+ assertProtoArrayEquals(
+ new ImsDedicatedBearerEvent[] {mImsDedicatedBearerEvent1}, outputs);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsDedicatedBearerEvent_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addImsDedicatedBearerEvent(mImsDedicatedBearerEvent1);
+
+ mPersistAtomsStorage.addImsDedicatedBearerEvent(mImsDedicatedBearerEvent2);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ ImsDedicatedBearerEvent[] output =
+ mPersistAtomsStorage.getImsDedicatedBearerEvent(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsDedicatedBearerEvent[] {
+ mImsDedicatedBearerEvent1, mImsDedicatedBearerEvent2}, output);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsDedicatedBearerEvent_withSameProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ mPersistAtomsStorage.addImsDedicatedBearerEvent(mImsDedicatedBearerEvent1);
+ mPersistAtomsStorage.addImsDedicatedBearerEvent(mImsDedicatedBearerEvent1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // The least recent (the first) registration termination should be evicted
+ verifyCurrentStateSavedToFileOnce();
+ ImsDedicatedBearerEvent[] output =
+ mPersistAtomsStorage.getImsDedicatedBearerEvent(0L);
+ assertEquals(mImsDedicatedBearerEvent1.carrierId, output[0].carrierId);
+ assertEquals(mImsDedicatedBearerEvent1.slotId, output[0].slotId);
+ assertEquals(mImsDedicatedBearerEvent1.ratAtEnd, output[0].ratAtEnd);
+ assertEquals(mImsDedicatedBearerEvent1.qci, output[0].qci);
+ assertEquals(mImsDedicatedBearerEvent1.localConnectionInfoReceived,
+ output[0].localConnectionInfoReceived);
+ assertEquals(mImsDedicatedBearerEvent1.remoteConnectionInfoReceived,
+ output[0].remoteConnectionInfoReceived);
+ assertEquals(mImsDedicatedBearerEvent1.hasListeners,
+ output[0].hasListeners);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsDedicatedBearerEvent_tooManyEntries() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ // Add 11 stats, but max is 10
+ for (int i = 0; i < 11; i++) {
+ mPersistAtomsStorage.addImsDedicatedBearerEvent(mImsDedicatedBearerEvent1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+ mPersistAtomsStorage.addImsDedicatedBearerEvent(mImsDedicatedBearerEvent2);
+
+ verifyCurrentStateSavedToFileOnce();
+ ImsDedicatedBearerEvent[] stats =
+ mPersistAtomsStorage.getImsDedicatedBearerEvent(0L);
+ assertHasStatsAndCount(stats, mImsDedicatedBearerEvent1, 11);
+ assertHasStatsAndCount(stats, mImsDedicatedBearerEvent2, 1);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsDedicatedBearerEvent_updateExistingEntries() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ ImsDedicatedBearerEvent newStats = copyOf(mImsDedicatedBearerEvent1);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ mPersistAtomsStorage.addImsDedicatedBearerEvent(copyOf(mImsDedicatedBearerEvent1));
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // mImsDedicatedBearerEvent1's count should be doubled
+ verifyCurrentStateSavedToFileOnce();
+ ImsDedicatedBearerEvent[] stats =
+ mPersistAtomsStorage.getImsDedicatedBearerEvent(0L);
+ newStats.count *= 2;
+ assertProtoArrayEqualsIgnoringOrder(new ImsDedicatedBearerEvent[] {
+ mImsDedicatedBearerEvent2, newStats}, stats);
+ }
+
+
+ @Test
+ @SmallTest
+ public void addUceEventStats_emptyProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addUceEventStats(mUceEventStats1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ UceEventStats[] outputs = mPersistAtomsStorage.getUceEventStats(0L);
+ assertProtoArrayEquals(
+ new UceEventStats[] {mUceEventStats1}, outputs);
+ }
+
+ @Test
+ @SmallTest
+ public void addUceEventStats_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addUceEventStats(mUceEventStats1);
+
+ mPersistAtomsStorage.addUceEventStats(mUceEventStats2);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ UceEventStats[] output = mPersistAtomsStorage.getUceEventStats(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new UceEventStats[] {mUceEventStats1, mUceEventStats2}, output);
+ }
+
+ @Test
+ @SmallTest
+ public void addUceEventStats_withSameProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ mPersistAtomsStorage.addUceEventStats(mUceEventStats1);
+ mPersistAtomsStorage.addUceEventStats(mUceEventStats1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // The least recent (the first) registration termination should be evicted
+ verifyCurrentStateSavedToFileOnce();
+ UceEventStats[] output = mPersistAtomsStorage.getUceEventStats(0L);
+ assertEquals(mUceEventStats1.carrierId, output[0].carrierId);
+ assertEquals(mUceEventStats1.slotId, output[0].slotId);
+ assertEquals(mUceEventStats1.type, output[0].type);
+ assertEquals(mUceEventStats1.successful, output[0].successful);
+ assertEquals(mUceEventStats1.commandCode, output[0].commandCode);
+ assertEquals(mUceEventStats1.networkResponse, output[0].networkResponse);
+ assertEquals(2, output[0].count);
+ }
+
+ @Test
+ @SmallTest
+ public void addPresenceNotifyEvent_withSameProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ PresenceNotifyEvent event1 = new PresenceNotifyEvent();
+ event1.carrierId = CARRIER1_ID;
+ event1.slotId = SLOT_ID1;
+ event1.reason = 1;
+ event1.contentBodyReceived = true;
+ event1.rcsCapsCount = 1;
+ event1.mmtelCapsCount = 1;
+ event1.noCapsCount = 0;
+ event1.count = 1;
+
+ PresenceNotifyEvent event2 = copyOf(event1);
+ event2.rcsCapsCount = 0;
+
+ mPersistAtomsStorage.addPresenceNotifyEvent(event1);
+ mPersistAtomsStorage.addPresenceNotifyEvent(event2);
+
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // The least recent (the first) registration termination should be evicted
+ verifyCurrentStateSavedToFileOnce();
+ PresenceNotifyEvent[] output = mPersistAtomsStorage.getPresenceNotifyEvent(0L);
+
+ assertEquals(event1.carrierId, output[0].carrierId);
+ assertEquals(event1.slotId, output[0].slotId);
+ assertEquals(event1.contentBodyReceived, output[0].contentBodyReceived);
+ assertEquals(1, output[0].rcsCapsCount);
+ assertEquals(2, output[0].mmtelCapsCount);
+ assertEquals(2, output[0].count);
+
+ }
+ @Test
+ @SmallTest
+ public void addPresenceNotifyEvent_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addPresenceNotifyEvent(mPresenceNotifyEvent1);
+ mPersistAtomsStorage.addPresenceNotifyEvent(mPresenceNotifyEvent2);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ PresenceNotifyEvent[] output = mPersistAtomsStorage.getPresenceNotifyEvent(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new PresenceNotifyEvent[] {mPresenceNotifyEvent1, mPresenceNotifyEvent2}, output);
+ }
+
+ @Test
+ @SmallTest
+ public void getPresenceNotifyEvent_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ PresenceNotifyEvent[] output = mPersistAtomsStorage.getPresenceNotifyEvent(100L);
+
+ // should be denied
+ assertNull(output);
+ }
+
+ @Test
+ @SmallTest
+ public void getPresenceNotifyEvent_withSavedAtoms() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ PresenceNotifyEvent[] output1 = mPersistAtomsStorage.getPresenceNotifyEvent(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ PresenceNotifyEvent[] output2 = mPersistAtomsStorage.getPresenceNotifyEvent(50L);
+
+ // first set of results should equal to file contents, second should be empty, corresponding
+ // pull timestamp should be updated and saved
+ assertProtoArrayEqualsIgnoringOrder(
+ new PresenceNotifyEvent[] {mPresenceNotifyEvent1, mPresenceNotifyEvent2}, output1);
+ assertProtoArrayEquals(new PresenceNotifyEvent[0], output2);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ mPersistAtomsStorage.getAtomsProto().presenceNotifyEventPullTimestampMillis);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(
+ START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).presenceNotifyEventPullTimestampMillis);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).presenceNotifyEventPullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void addSipTransportFeatureTag_emptyProto() throws Exception {
+ // verify add atom into new file
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSipTransportFeatureTagStats(mSipTransportFeatureTagStats1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ verifyCurrentStateSavedToFileOnce();
+
+ SipTransportFeatureTagStats[] outputs =
+ mPersistAtomsStorage.getSipTransportFeatureTagStats(0L);
+ assertProtoArrayEquals(
+ new SipTransportFeatureTagStats[] {mSipTransportFeatureTagStats1}, outputs);
+ }
+
+ @Test
+ @SmallTest
+ public void addSipTransportFeatureTagStats_withExistingEntries() throws Exception {
+ // verify add atom on existing atom already stored
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ //Add two different SipTransportFeatureTagStats.
+ mPersistAtomsStorage.addSipTransportFeatureTagStats(mSipTransportFeatureTagStats1);
+ mPersistAtomsStorage.addSipTransportFeatureTagStats(mSipTransportFeatureTagStats2);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // SipTransportFeatureTagStats should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ SipTransportFeatureTagStats[] outputs =
+ mPersistAtomsStorage.getSipTransportFeatureTagStats(0L);
+
+ assertProtoArrayEqualsIgnoringOrder(new SipTransportFeatureTagStats[]
+ {mSipTransportFeatureTagStats1, mSipTransportFeatureTagStats2}, outputs);
+ }
+
+ @Test
+ @SmallTest
+ public void addSipTransportFeatureTagStats_tooManyEntries() throws Exception {
+ // verify add atom excess MAX count (100)
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ // Try to add 26 stats where MAX is 25
+ int max = 26;
+ SipTransportFeatureTagStats[] overMaxSipTransportFeatureTagStats =
+ new SipTransportFeatureTagStats[max];
+
+ for (int i = 0; i < max; i++) {
+ overMaxSipTransportFeatureTagStats[i] = copyOf(mSipTransportFeatureTagStats1);
+ overMaxSipTransportFeatureTagStats[i].sipTransportDeniedReason = i;
+ mPersistAtomsStorage
+ .addSipTransportFeatureTagStats(overMaxSipTransportFeatureTagStats[i]);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+
+ mPersistAtomsStorage
+ .addSipTransportFeatureTagStats(mSipTransportFeatureTagStats2);
+ verifyCurrentStateSavedToFileOnce();
+
+ SipTransportFeatureTagStats[] outputs =
+ mPersistAtomsStorage.getSipTransportFeatureTagStats(0L);
+
+ // The last added SipTransportFeatureTagStat remains
+ // and two old stats should be removed
+ assertHasStats(outputs, overMaxSipTransportFeatureTagStats, max - 2);
+ assertHasStats(outputs, mSipTransportFeatureTagStats2, 1);
+ }
+
+ @Test
+ @SmallTest
+ public void addSipTransportFeatureTagStats_updateExistingEntries() throws Exception {
+ // verify count
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSipTransportFeatureTagStats(copyOf(mSipTransportFeatureTagStats1));
+ mPersistAtomsStorage.incTimeMillis(100L);
+ verifyCurrentStateSavedToFileOnce();
+
+ // SipTransportFeatureTag's durations should be doubled
+ SipTransportFeatureTagStats newSipTransportFeatureTagStats1 =
+ copyOf(mSipTransportFeatureTagStats1);
+ newSipTransportFeatureTagStats1.associatedMillis *= 2;
+
+ SipTransportFeatureTagStats[] outputs =
+ mPersistAtomsStorage.getSipTransportFeatureTagStats(0L);
+
+ assertProtoArrayEqualsIgnoringOrder(
+ new SipTransportFeatureTagStats[] {
+ newSipTransportFeatureTagStats1,
+ mSipTransportFeatureTagStats2
+ }, outputs);
+ }
+
+ @Test
+ @SmallTest
+ public void getSipTransportFeatureTagStats_tooFrequent() throws Exception {
+ // verify get frequently
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+
+ SipTransportFeatureTagStats[] outputs =
+ mPersistAtomsStorage.getSipTransportFeatureTagStats(100L);
+
+ // should be denied
+ assertNull(outputs);
+ }
+
+ @Test
+ @SmallTest
+ public void getSipTransportFeatureTagStats_withSavedAtoms() throws Exception {
+ // verify last get time after get atoms
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ SipTransportFeatureTagStats[] output1 =
+ mPersistAtomsStorage.getSipTransportFeatureTagStats(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ SipTransportFeatureTagStats[] output2 =
+ mPersistAtomsStorage.getSipTransportFeatureTagStats(50L);
+
+ // first set of results should equal to file contents, second should be empty, corresponding
+ // pull timestamp should be updated and saved
+ assertProtoArrayEqualsIgnoringOrder(
+ new SipTransportFeatureTagStats[] {
+ mSipTransportFeatureTagStats1,
+ mSipTransportFeatureTagStats2
+ }, output1);
+ assertProtoArrayEquals(new SipTransportFeatureTagStats[0], output2);
+ assertEquals(START_TIME_MILLIS + 200L,
+ mPersistAtomsStorage.getAtomsProto()
+ .sipTransportFeatureTagStatsPullTimestampMillis);
+
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).sipTransportFeatureTagStatsPullTimestampMillis);
+ assertEquals(START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).sipTransportFeatureTagStatsPullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void addSipDelegateStats_emptyProto() throws Exception {
+ // verify add atom into new file
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSipDelegateStats(mSipDelegateStats1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ verifyCurrentStateSavedToFileOnce();
+
+ SipDelegateStats[] outputs = mPersistAtomsStorage.getSipDelegateStats(0L);
+ assertProtoArrayEquals(new SipDelegateStats[] {mSipDelegateStats1}, outputs);
+ }
+
+ @Test
+ @SmallTest
+ public void addSipDelegateStats_withExistingEntries() throws Exception {
+ // verify add atom on existing atom already stored
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSipDelegateStats(copyOf(mSipDelegateStats1));
+ mPersistAtomsStorage.addSipDelegateStats(copyOf(mSipDelegateStats2));
+ mPersistAtomsStorage.addSipDelegateStats(copyOf(mSipDelegateStats3));
+ mPersistAtomsStorage.incTimeMillis(100L);
+ // Three SipDelegateStats should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+
+ SipDelegateStats[] outputs =
+ mPersistAtomsStorage.getSipDelegateStats(0L);
+
+ assertProtoArrayEqualsIgnoringOrder(
+ new SipDelegateStats[] {mSipDelegateStats1, mSipDelegateStats2, mSipDelegateStats3},
+ outputs);
+ }
+
+ @Test
+ @SmallTest
+ public void addSipDelegateStats_tooManyEntries() throws Exception {
+ // verify add atom excess MAX count
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ // Try to add 11 stats where MAX is 10
+ int max = 11;
+ SipDelegateStats[] overMaxSipDelegateStats = new SipDelegateStats[max];
+ for (int i = 0; i < max; i++) {
+ overMaxSipDelegateStats[i] = copyOf(mSipDelegateStats1);
+ mPersistAtomsStorage
+ .addSipDelegateStats(overMaxSipDelegateStats[i]);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+ mPersistAtomsStorage.addSipDelegateStats(mSipDelegateStats3);
+ verifyCurrentStateSavedToFileOnce();
+
+ SipDelegateStats[] outputs =
+ mPersistAtomsStorage.getSipDelegateStats(0L);
+
+ // The last added SipDelegate remains
+ // and two old stats should be removed
+ assertHasStats(outputs, overMaxSipDelegateStats, max - 2);
+ assertHasStats(outputs, mSipDelegateStats3, 1);
+ }
+
+ @Test
+ @SmallTest
+ public void addSipDelegateStats_updateExistingEntries() throws Exception {
+ // verify count
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ SipDelegateStats newSipDelegateStats3 = copyOf(mSipDelegateStats3);
+ newSipDelegateStats3.destroyReason =
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD;
+ mPersistAtomsStorage.addSipDelegateStats(newSipDelegateStats3);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ SipDelegateStats newSipDelegateStats1 = copyOf(mSipDelegateStats1);
+ newSipDelegateStats1.destroyReason =
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP;
+ mPersistAtomsStorage.addSipDelegateStats(newSipDelegateStats1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ verifyCurrentStateSavedToFileOnce();
+
+ SipDelegateStats[] outputs = mPersistAtomsStorage.getSipDelegateStats(0L);
+
+ assertProtoArrayEqualsIgnoringOrder(
+ new SipDelegateStats[] {mSipDelegateStats2, mSipDelegateStats3,
+ newSipDelegateStats3, newSipDelegateStats1}, outputs);
+ }
+
+ @Test
+ @SmallTest
+ public void getSipDelegateStats_tooFrequent() throws Exception {
+ // verify get frequently
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+
+ SipDelegateStats[] outputs = mPersistAtomsStorage.getSipDelegateStats(100L);
+ // should be denied
+ assertNull(outputs);
+ }
+
+ @Test
+ @SmallTest
+ public void getSipDelegateStats_withSavedAtoms() throws Exception {
+ // verify last get time after get atoms
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ SipDelegateStats[] output1 = mPersistAtomsStorage.getSipDelegateStats(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ SipDelegateStats[] output2 = mPersistAtomsStorage.getSipDelegateStats(50L);
+
+ // first set of results should equal to file contents, second should be empty, corresponding
+ // pull timestamp should be updated and saved
+ assertProtoArrayEqualsIgnoringOrder(
+ new SipDelegateStats[] {
+ mSipDelegateStats2,
+ mSipDelegateStats3}, output1);
+ assertProtoArrayEquals(new SipDelegateStats[0], output2);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ mPersistAtomsStorage.getAtomsProto().sipDelegateStatsPullTimestampMillis);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(
+ START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).sipDelegateStatsPullTimestampMillis);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).sipDelegateStatsPullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void addGbaEvent_emptyProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addGbaEvent(mGbaEvent1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // gba event should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ GbaEvent[] stats = mPersistAtomsStorage.getGbaEvent(0L);
+ assertProtoArrayEquals(new GbaEvent[] {mGbaEvent1}, stats);
+ }
+
+ @Test
+ @SmallTest
+ public void addGbaEvent_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addGbaEvent(mGbaEvent1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ mPersistAtomsStorage.addGbaEvent(mGbaEvent2);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // gba event1, gba event2 should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ GbaEvent[] stats = mPersistAtomsStorage.getGbaEvent(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new GbaEvent[] {mGbaEvent1, mGbaEvent2}, stats);
+ }
+
+ @Test
+ @SmallTest
+ public void addGbaEvent_tooManyEntries() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ // Add 11 stats, but max is 10
+ for (int i = 0; i < 11; i++) {
+ mPersistAtomsStorage.addGbaEvent(mGbaEvent1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+ mPersistAtomsStorage.addGbaEvent(mGbaEvent2);
+
+ verifyCurrentStateSavedToFileOnce();
+ GbaEvent[] stats = mPersistAtomsStorage.getGbaEvent(0L);
+ assertHasStatsAndCount(stats, mGbaEvent1, 11);
+ assertHasStatsAndCount(stats, mGbaEvent2, 1);
+ }
+
+ @Test
+ @SmallTest
+ public void addGbaEvent_updateExistingEntries() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ GbaEvent newStats = copyOf(mGbaEvent1);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ mPersistAtomsStorage.addGbaEvent(copyOf(mGbaEvent1));
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // mGbaEvent1's count should be doubled
+ verifyCurrentStateSavedToFileOnce();
+ GbaEvent[] stats =
+ mPersistAtomsStorage.getGbaEvent(0L);
+ newStats.count *= 2;
+ assertProtoArrayEqualsIgnoringOrder(new GbaEvent[] {mGbaEvent2, newStats}, stats);
+ }
+
+ @Test
+ @SmallTest
+ public void addSipMessageResponse_emptyProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSipMessageResponse(mSipMessageResponse1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ verifyCurrentStateSavedToFileOnce();
+ SipMessageResponse[] expected = mPersistAtomsStorage.getSipMessageResponse(0L);
+ assertProtoArrayEquals(new SipMessageResponse[] {mSipMessageResponse1}, expected);
+ }
+
+ @Test
+ @SmallTest
+ public void addSipMessageResponse_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSipMessageResponse(mSipMessageResponse1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ mPersistAtomsStorage.addSipMessageResponse(mSipMessageResponse2);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ verifyCurrentStateSavedToFileOnce();
+ SipMessageResponse[] expected =
+ new SipMessageResponse[] {mSipMessageResponse1, mSipMessageResponse2};
+
+ assertProtoArrayEqualsIgnoringOrder(
+ expected, mPersistAtomsStorage.getSipMessageResponse(0L));
+ }
+
+ @Test
+ @SmallTest
+ public void addSipMessageResponse_tooManyEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ // store 11 same atoms, but only 1 atoms stored with count 11
+ for (int i = 0; i < 11; i++) {
+ mPersistAtomsStorage.addSipMessageResponse(mSipMessageResponse1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+ // store 1 different atom and count 1
+ mPersistAtomsStorage.addSipMessageResponse(mSipMessageResponse2);
+
+ verifyCurrentStateSavedToFileOnce();
+ SipMessageResponse[] result = mPersistAtomsStorage.getSipMessageResponse(0L);
+
+ // first atom has count 11, the other has 1
+ assertHasStats(result, mSipMessageResponse1, 11);
+ assertHasStats(result, mSipMessageResponse2, 1);
+ }
+
+ @Test
+ @SmallTest
+ public void addSipMessageResponse_updateExistingEntries() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addSipMessageResponse(copyOf(mSipMessageResponse2));
+ mPersistAtomsStorage.incTimeMillis(100L);
+ verifyCurrentStateSavedToFileOnce();
+
+ SipMessageResponse[] outputs = mPersistAtomsStorage.getSipMessageResponse(0L);
+ SipMessageResponse newSipMessageResponse = copyOf(mSipMessageResponse2);
+ newSipMessageResponse.count *= 2;
+ assertProtoArrayEqualsIgnoringOrder(
+ new SipMessageResponse[] {mSipMessageResponse1, newSipMessageResponse}, outputs);
+ }
+
+ @Test
+ @SmallTest
+ public void addCompleteSipTransportSession_emptyProto() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addCompleteSipTransportSession(mSipTransportSession1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ verifyCurrentStateSavedToFileOnce();
+ SipTransportSession[] expected = mPersistAtomsStorage.getSipTransportSession(0L);
+ assertProtoArrayEquals(new SipTransportSession[] {mSipTransportSession1}, expected);
+ }
+
+ @Test
+ @SmallTest
+ public void addCompleteSipTransportSession_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addCompleteSipTransportSession(mSipTransportSession1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ mPersistAtomsStorage.addCompleteSipTransportSession(mSipTransportSession2);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ verifyCurrentStateSavedToFileOnce();
+ SipTransportSession[] expected =
+ new SipTransportSession[] {mSipTransportSession1, mSipTransportSession2};
+
+ assertProtoArrayEqualsIgnoringOrder(
+ expected, mPersistAtomsStorage.getSipTransportSession(0L));
+ }
+
+ @Test
+ @SmallTest
+ public void addCompleteSipTransportSession_tooManyEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ // store 11 same atoms, but only 1 atoms stored with count 11
+ for (int i = 0; i < 11; i++) {
+ mPersistAtomsStorage.addCompleteSipTransportSession(mSipTransportSession1);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+ // store 1 different atom and count 1
+ mPersistAtomsStorage.addCompleteSipTransportSession(mSipTransportSession2);
+
+ verifyCurrentStateSavedToFileOnce();
+ SipTransportSession[] result = mPersistAtomsStorage.getSipTransportSession(0L);
+
+ // first atom has count 11, the other has 1
+ assertHasStats(result, mSipTransportSession1, 11);
+ assertHasStats(result, mSipTransportSession2, 1);
+ }
+
+ @Test
+ @SmallTest
+ public void addCompleteSipTransportSession_updateExistingEntries() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addCompleteSipTransportSession(copyOf(mSipTransportSession2));
+ mPersistAtomsStorage.incTimeMillis(100L);
+ verifyCurrentStateSavedToFileOnce();
+
+ SipTransportSession[] outputs = mPersistAtomsStorage.getSipTransportSession(0L);
+ SipTransportSession newSipTransportSession = copyOf(mSipTransportSession2);
+ newSipTransportSession.sessionCount *= 2;
+ newSipTransportSession.endedGracefullyCount *= 2;
+ assertProtoArrayEqualsIgnoringOrder(
+ new SipTransportSession[] {mSipTransportSession1,
+ newSipTransportSession}, outputs);
}
/* Utilities */
@@ -1367,6 +2994,32 @@
atoms.imsRegistrationStats = mImsRegistrationStats;
atoms.imsRegistrationTerminationPullTimestampMillis = lastPullTimeMillis;
atoms.imsRegistrationTermination = mImsRegistrationTerminations;
+ atoms.imsRegistrationFeatureTagStatsPullTimestampMillis = lastPullTimeMillis;
+ atoms.imsRegistrationFeatureTagStats = mImsRegistrationFeatureTagStatses;
+ atoms.rcsClientProvisioningStatsPullTimestampMillis = lastPullTimeMillis;
+ atoms.rcsClientProvisioningStats = mRcsClientProvisioningStatses;
+ atoms.rcsAcsProvisioningStatsPullTimestampMillis = lastPullTimeMillis;
+ atoms.rcsAcsProvisioningStats = mRcsAcsProvisioningStatses;
+ atoms.imsRegistrationServiceDescStatsPullTimestampMillis = lastPullTimeMillis;
+ atoms.imsRegistrationServiceDescStats = mImsRegistrationServiceDescStats;
+ atoms.imsDedicatedBearerListenerEventPullTimestampMillis = lastPullTimeMillis;
+ atoms.imsDedicatedBearerListenerEvent = mImsDedicatedBearerListenerEvents;
+ atoms.imsDedicatedBearerEventPullTimestampMillis = lastPullTimeMillis;
+ atoms.imsDedicatedBearerEvent = mImsDedicatedBearerEvents;
+ atoms.uceEventStatsPullTimestampMillis = lastPullTimeMillis;
+ atoms.uceEventStats = mUceEventStatses;
+ atoms.presenceNotifyEventPullTimestampMillis = lastPullTimeMillis;
+ atoms.presenceNotifyEvent = mPresenceNotifyEvents;
+ atoms.sipTransportFeatureTagStatsPullTimestampMillis = lastPullTimeMillis;
+ atoms.sipTransportFeatureTagStats = mSipTransportFeatureTagStatsArray;
+ atoms.sipDelegateStatsPullTimestampMillis = lastPullTimeMillis;
+ atoms.sipDelegateStats = mSipDelegateStatsArray;
+ atoms.gbaEventPullTimestampMillis = lastPullTimeMillis;
+ atoms.gbaEvent = mGbaEvent;
+ atoms.sipMessageResponsePullTimestampMillis = lastPullTimeMillis;
+ atoms.sipMessageResponse = mSipMessageResponse;
+ atoms.sipTransportSessionPullTimestampMillis = lastPullTimeMillis;
+ atoms.sipTransportSession = mSipTransportSession;
FileOutputStream stream = new FileOutputStream(mTestFile);
stream.write(PersistAtoms.toByteArray(atoms));
stream.close();
@@ -1426,6 +3079,65 @@
return DataCallSession.parseFrom(MessageNano.toByteArray(source));
}
+ private static ImsRegistrationFeatureTagStats copyOf(ImsRegistrationFeatureTagStats source)
+ throws Exception {
+ return ImsRegistrationFeatureTagStats.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static RcsAcsProvisioningStats copyOf(RcsAcsProvisioningStats source)
+ throws Exception {
+ return RcsAcsProvisioningStats.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static ImsRegistrationServiceDescStats copyOf(ImsRegistrationServiceDescStats source)
+ throws Exception {
+ return ImsRegistrationServiceDescStats.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static ImsDedicatedBearerListenerEvent copyOf(ImsDedicatedBearerListenerEvent source)
+ throws Exception {
+ return ImsDedicatedBearerListenerEvent.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static ImsDedicatedBearerEvent copyOf(ImsDedicatedBearerEvent source)
+ throws Exception {
+ return ImsDedicatedBearerEvent.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static UceEventStats copyOf(UceEventStats source)
+ throws Exception {
+ return UceEventStats.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static PresenceNotifyEvent copyOf(PresenceNotifyEvent source)
+ throws Exception {
+ return PresenceNotifyEvent.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static SipDelegateStats copyOf(SipDelegateStats source)
+ throws Exception {
+ return SipDelegateStats.parseFrom(MessageNano.toByteArray(source));
+ }
+ private static SipTransportFeatureTagStats copyOf(SipTransportFeatureTagStats source)
+ throws Exception {
+ return SipTransportFeatureTagStats.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static GbaEvent copyOf(GbaEvent source)
+ throws Exception {
+ return GbaEvent.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static SipMessageResponse copyOf(SipMessageResponse source)
+ throws Exception {
+ return SipMessageResponse.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static SipTransportSession copyOf(SipTransportSession source)
+ throws Exception {
+ return SipTransportSession.parseFrom(MessageNano.toByteArray(source));
+ }
+
private void assertAllPullTimestampEquals(long timestamp) {
assertEquals(
timestamp,
@@ -1496,4 +3208,213 @@
inOrder.verify(mTestFileOutputStream, times(1)).close();
inOrder.verifyNoMoreInteractions();
}
+
+ private static void assertHasStatsCountTime(
+ ImsRegistrationFeatureTagStats[] statses,
+ @Nullable ImsRegistrationFeatureTagStats expectedStats,
+ int expectedCount, long expectedTime) {
+ assertNotNull(statses);
+ int actualCount = 0;
+ long actualTime = 0;
+ for (ImsRegistrationFeatureTagStats stats : statses) {
+ if (stats != null && expectedStats != null) {
+ if (stats.carrierId == expectedStats.carrierId
+ && stats.slotId == expectedStats.slotId
+ && stats.featureTagName == expectedStats.featureTagName
+ && stats.registrationTech == expectedStats.registrationTech) {
+ actualCount++;
+ actualTime += stats.registeredMillis;
+ }
+ }
+ }
+ assertEquals(expectedCount, actualCount);
+ assertEquals(expectedTime, actualTime);
+ }
+
+ private static void assertHasStatsAndCount(
+ RcsClientProvisioningStats[] statses,
+ @Nullable RcsClientProvisioningStats expectedStats, int expectedCount) {
+ assertNotNull(statses);
+ int actualCount = -1;
+ for (RcsClientProvisioningStats stats : statses) {
+ if (stats.carrierId == expectedStats.carrierId
+ && stats.slotId == expectedStats.slotId
+ && stats.event == expectedStats.event) {
+ actualCount = stats.count;
+ }
+ }
+ assertEquals(expectedCount, actualCount);
+ }
+
+ private static void assertHasStats(
+ ImsDedicatedBearerListenerEvent[] statses,
+ @Nullable ImsDedicatedBearerListenerEvent expectedStats, int expectedCount) {
+ assertNotNull(statses);
+ int actualCount = 0;
+ for (ImsDedicatedBearerListenerEvent stats : statses) {
+ if (stats != null && expectedStats != null) {
+ if (MessageNano.messageNanoEquals(stats, expectedStats)) {
+ actualCount++;
+ }
+ }
+ }
+ assertEquals(expectedCount, actualCount);
+ }
+
+ private static void assertHasStatsAndCount(
+ ImsDedicatedBearerEvent[] statses,
+ @Nullable ImsDedicatedBearerEvent expectedStats, int expectedCount) {
+ assertNotNull(statses);
+ int actualCount = -1;
+ for (ImsDedicatedBearerEvent stats : statses) {
+ if (stats.carrierId == expectedStats.carrierId
+ && stats.slotId == expectedStats.slotId
+ && stats.ratAtEnd == expectedStats.ratAtEnd
+ && stats.qci == expectedStats.qci
+ && stats.bearerState == expectedStats.bearerState
+ && stats.localConnectionInfoReceived
+ == expectedStats.localConnectionInfoReceived
+ && stats.remoteConnectionInfoReceived
+ == expectedStats.remoteConnectionInfoReceived
+ && stats.hasListeners == expectedStats.hasListeners) {
+ actualCount = stats.count;
+ }
+ }
+ assertEquals(expectedCount, actualCount);
+ }
+
+ private static void assertHasStatsAndCountDuration(
+ RcsAcsProvisioningStats[] statses,
+ @Nullable RcsAcsProvisioningStats expectedStats, int count, long duration) {
+ assertNotNull(statses);
+ int actualCount = -1;
+ long actualDuration = -1;
+ for (RcsAcsProvisioningStats stats : statses) {
+ if (stats.carrierId == expectedStats.carrierId
+ && stats.slotId == expectedStats.slotId
+ && stats.responseCode == expectedStats.responseCode
+ && stats.responseType == expectedStats.responseType
+ && stats.isSingleRegistrationEnabled
+ == expectedStats.isSingleRegistrationEnabled) {
+ actualCount = stats.count;
+ actualDuration = stats.stateTimerMillis;
+ }
+ }
+ assertEquals(count, actualCount);
+ assertEquals(duration, actualDuration);
+ }
+
+ private static void assertHasStats(SipDelegateStats[] results,
+ Object expectedStats, int expectedCount) {
+ assertNotNull(results);
+ assertNotNull(expectedStats);
+
+ int realCount = 0;
+ if (expectedStats instanceof SipDelegateStats[]) {
+ SipDelegateStats[] expectedResults = (SipDelegateStats[]) expectedStats;
+ for (SipDelegateStats stat: results) {
+ for (SipDelegateStats estat : expectedResults) {
+ if (stat != null && estat != null) {
+ if (MessageNano.messageNanoEquals(stat, estat)) {
+ realCount++;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ SipDelegateStats expectedResult = (SipDelegateStats) expectedStats;
+ for (SipDelegateStats stat : results) {
+ if (stat != null && expectedStats != null) {
+ if (MessageNano.messageNanoEquals(stat, expectedResult)) {
+ realCount++;
+ }
+ }
+ }
+ }
+ assertEquals(expectedCount, realCount);
+ }
+
+ private static void assertHasStats(SipTransportFeatureTagStats[] results,
+ Object expectedStats, int expectedCount) {
+ assertNotNull(results);
+ assertNotNull(expectedStats);
+
+ int realCount = 0;
+ if (expectedStats instanceof SipTransportFeatureTagStats[]) {
+ SipTransportFeatureTagStats[] expectedResults =
+ (SipTransportFeatureTagStats[]) expectedStats;
+ for (SipTransportFeatureTagStats stat: results) {
+ for (SipTransportFeatureTagStats estat : expectedResults) {
+ if (stat != null && estat != null) {
+ if (MessageNano.messageNanoEquals(stat, estat)) {
+ realCount++;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ SipTransportFeatureTagStats expectedResult =
+ (SipTransportFeatureTagStats) expectedStats;
+ for (SipTransportFeatureTagStats stat : results) {
+ if (stat != null && expectedStats != null) {
+ if (MessageNano.messageNanoEquals(stat, expectedResult)) {
+ realCount++;
+ }
+ }
+ }
+ }
+ assertEquals(expectedCount, realCount);
+ }
+
+ private static void assertHasStatsAndCount(
+ GbaEvent[] statses,
+ @Nullable GbaEvent expectedStats, int expectedCount) {
+ assertNotNull(statses);
+ int actualCount = -1;
+ for (GbaEvent stats : statses) {
+ if (stats.carrierId == expectedStats.carrierId
+ && stats.slotId == expectedStats.slotId
+ && stats.successful == expectedStats.successful
+ && stats.failedReason == expectedStats.failedReason) {
+ actualCount = stats.count;
+ }
+ }
+ assertEquals(expectedCount, actualCount);
+ }
+
+ private static void assertHasStats(
+ SipMessageResponse[] statses,
+ @Nullable SipMessageResponse expectedStats, int expectedCount) {
+ assertNotNull(statses);
+ int actualCount = -1;
+ for (SipMessageResponse stats : statses) {
+ if (stats.carrierId == expectedStats.carrierId
+ && stats.slotId == expectedStats.slotId
+ && stats.sipMessageMethod == expectedStats.sipMessageMethod
+ && stats.sipMessageResponse == expectedStats.sipMessageResponse
+ && stats.sipMessageDirection == expectedStats.sipMessageDirection) {
+ actualCount = stats.count;
+ }
+ }
+ assertEquals(expectedCount, actualCount);
+ }
+
+ private static void assertHasStats(
+ SipTransportSession[] statses,
+ @Nullable SipTransportSession expectedStats, int expectedCount) {
+ assertNotNull(statses);
+ int actualCount = -1;
+ for (SipTransportSession stats : statses) {
+ if (stats.carrierId == expectedStats.carrierId
+ && stats.slotId == expectedStats.slotId
+ && stats.sessionMethod == expectedStats.sessionMethod
+ && stats.sipMessageDirection == expectedStats.sipMessageDirection
+ && stats.sipResponse == expectedStats.sipResponse) {
+ actualCount = stats.sessionCount;
+ }
+ }
+ assertEquals(expectedCount, actualCount);
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/RcsStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/RcsStatsTest.java
new file mode 100644
index 0000000..68fd017
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/RcsStatsTest.java
@@ -0,0 +1,1253 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.metrics;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.telephony.TelephonyProtoEnums;
+import android.telephony.ims.DelegateRegistrationState;
+import android.telephony.ims.FeatureTagState;
+import android.telephony.ims.SipDelegateManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.ims.rcs.uce.util.FeatureTags;
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.nano.PersistAtomsProto.GbaEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsDedicatedBearerListenerEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationFeatureTagStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationServiceDescStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.PresenceNotifyEvent;
+import com.android.internal.telephony.nano.PersistAtomsProto.RcsAcsProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.RcsClientProvisioningStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipDelegateStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipMessageResponse;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportFeatureTagStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.SipTransportSession;
+import com.android.internal.telephony.nano.PersistAtomsProto.UceEventStats;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+public class RcsStatsTest extends TelephonyTest {
+ private static final String TAG = RcsStatsTest.class.getSimpleName();
+
+ private static final long START_TIME_MILLIS = 2000L;
+ private static final int SLOT_ID = 0;
+ private static final int SLOT2_ID = 1;
+ private static final int INVALID_SLOT_ID = -1;
+ private static final int CARRIER_ID = 100;
+ private static final int CARRIER2_ID = 200;
+ private static final int INVALID_CARRIER_ID = -1;
+ private static final int INVALID_SUB_ID = Integer.MIN_VALUE;
+
+ private class TestResult {
+ public String tagName;
+ public int tagValue;
+ public long duration;
+ public int deniedReason;
+ public int deregiReason;
+ TestResult(String tagName, int tagValue, long duration,
+ int deniedReason, int deregiReason) {
+ this.tagName = tagName;
+ this.tagValue = tagValue;
+ this.duration = duration;
+ this.deniedReason = deniedReason;
+ this.deregiReason = deregiReason;
+ }
+ }
+
+ private int mSubId = 10;
+ private int mSubId2 = 20;
+
+ private TestableRcsStats mRcsStats;
+
+ private class TestableRcsStats extends RcsStats {
+ private long mTimeMillis = START_TIME_MILLIS;
+ private boolean mEnabledInvalidSubId = false;
+
+ TestableRcsStats() {
+ super();
+ }
+
+ @Override
+ protected int getSlotId(int subId) {
+ if (mEnabledInvalidSubId) {
+ return INVALID_SLOT_ID;
+ }
+
+ if (subId == mSubId) {
+ return SLOT_ID;
+ } else if (subId == mSubId2) {
+ return SLOT2_ID;
+ }
+ return SLOT2_ID;
+ }
+
+ @Override
+ protected int getCarrierId(int subId) {
+ if (mEnabledInvalidSubId) {
+ return INVALID_CARRIER_ID;
+ }
+
+ if (subId == mSubId) {
+ return CARRIER_ID;
+ } else if (subId == mSubId2) {
+ return CARRIER2_ID;
+ }
+ return INVALID_CARRIER_ID;
+ }
+
+ @Override
+ protected boolean isValidCarrierId(int carrierId) {
+ if (carrierId == INVALID_CARRIER_ID) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected long getWallTimeMillis() {
+ // NOTE: super class constructor will be executed before private field is set, which
+ // gives the wrong start time (mTimeMillis will have its default value of 0L)
+ Log.d(TAG, "getWallTimeMillis return value : " + mTimeMillis);
+ return mTimeMillis == 0L ? START_TIME_MILLIS : mTimeMillis;
+ }
+
+ @Override
+ protected void logd(String msg) {
+ Log.w(TAG, msg);
+ }
+
+ @Override
+ protected int getSubId(int slotId) {
+ if (mEnabledInvalidSubId) {
+ return INVALID_SUB_ID;
+ }
+
+ if (slotId == SLOT_ID) {
+ return mSubId;
+ } else if (slotId == SLOT2_ID) {
+ return mSubId2;
+ }
+ return INVALID_SUB_ID;
+ }
+
+ public void setEnableInvalidSubId() {
+ mEnabledInvalidSubId = true;
+ }
+ private void setTimeMillis(long timeMillis) {
+ mTimeMillis = timeMillis;
+ }
+
+ private void incTimeMillis(long timeMillis) {
+ mTimeMillis += timeMillis;
+ Log.d(TAG, "incTimeMillis mTimeMillis : " + mTimeMillis);
+ }
+
+ public int getRcsAcsProvisioningCachedSize() {
+ return mRcsAcsProvisioningStatsList.size();
+ }
+
+ public int getImsRegistrationServiceDescCachedSize() {
+ return mImsRegistrationServiceDescStatsList.size();
+ }
+
+ public long getRcsAcsProvisioningCachedTime(int carreirId, int slotId) {
+ for (RcsAcsProvisioningStats stats : mRcsAcsProvisioningStatsList) {
+ if (stats.carrierId == carreirId && stats.slotId == slotId) {
+ return stats.stateTimerMillis;
+ }
+ }
+ return 0L;
+ }
+
+ public int getRcsProvisioningCallbackMapSize() {
+ return mRcsProvisioningCallbackMap.size();
+ }
+
+ public ImsDedicatedBearerListenerEvent dedicatedBearerListenerEventMap_get(
+ final int listenerId) {
+ return mDedicatedBearerListenerEventMap.get(listenerId);
+ }
+
+ public boolean dedicatedBearerListenerEventMap_containsKey(final int listenerId) {
+ return mDedicatedBearerListenerEventMap.containsKey(listenerId);
+ }
+
+ public void dedicatedBearerListenerEventMap_remove(final int listenerId) {
+ mDedicatedBearerListenerEventMap.remove(listenerId);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+
+ mRcsStats = new TestableRcsStats();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void onImsRegistrationFeatureTagStats_withAtoms() throws Exception {
+ int slotId = SLOT_ID;
+ int carrierId = CARRIER_ID;
+ List<String> featureTagList = Arrays.asList(
+ "+g.3gpp.iari-ref=\"urn%3Aurn-7%3A3gpp-application.ims.iari.rcse.im\"",
+ "+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.session\"",
+ "+g.3gpp.icsi-ref=\"hh%3Ashin%3A-b.a.b.o\"",
+ "+g.gsma.rcs.isbot"
+ );
+
+ int registrationTech = 0;
+
+ mRcsStats.onImsRegistrationFeatureTagStats(
+ mSubId, featureTagList, registrationTech);
+
+ mRcsStats.onStoreCompleteImsRegistrationFeatureTagStats(mSubId);
+
+ ArgumentCaptor<ImsRegistrationFeatureTagStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationFeatureTagStats.class);
+ verify(mPersistAtomsStorage, times(featureTagList.size()))
+ .addImsRegistrationFeatureTagStats(captor.capture());
+ List<ImsRegistrationFeatureTagStats> captorValues = captor.getAllValues();
+
+ assertEquals(captorValues.size(), featureTagList.size());
+ for (int index = 0; index < captorValues.size(); index++) {
+ ImsRegistrationFeatureTagStats stats = captorValues.get(index);
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ assertEquals(mRcsStats.convertTagNameToValue(featureTagList.get(index)),
+ stats.featureTagName);
+ assertEquals(registrationTech, stats.registrationTech);
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void onRcsClientProvisioningStats_withAtoms() throws Exception {
+ /*
+ * RCS_CLIENT_PROVISIONING_STATS__EVENT__CLIENT_PARAMS_SENT
+ * RCS_CLIENT_PROVISIONING_STATS__EVENT__TRIGGER_RCS_RECONFIGURATION
+ * RCS_CLIENT_PROVISIONING_STATS__EVENT__DMA_CHANGED
+ */
+ int event =
+ TelephonyStatsLog.RCS_CLIENT_PROVISIONING_STATS__EVENT__CLIENT_PARAMS_SENT;
+
+ mRcsStats.onRcsClientProvisioningStats(mSubId, event);
+
+ ArgumentCaptor<RcsClientProvisioningStats> captor =
+ ArgumentCaptor.forClass(RcsClientProvisioningStats.class);
+ verify(mPersistAtomsStorage).addRcsClientProvisioningStats(captor.capture());
+ RcsClientProvisioningStats stats = captor.getValue();
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ assertEquals(event, stats.event);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onRcsAcsProvisioningStats_withAtoms() throws Exception {
+ boolean isSingleRegistrationEnabled = true;
+ int[] responseCode = {200, 401};
+ /*
+ * RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__ERROR
+ * RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML
+ * RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PRE_PROVISIONING_XML
+ */
+ int[] responseType = {
+ TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML,
+ TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__ERROR};
+ int[] slotIds = {SLOT_ID, SLOT_ID};
+ int[] carrierIds = {CARRIER_ID, CARRIER_ID};
+
+ // this will be cached
+ mRcsStats.onRcsAcsProvisioningStats(
+ mSubId, responseCode[0], responseType[0], isSingleRegistrationEnabled);
+
+ long timeGap = 6000L;
+ mRcsStats.incTimeMillis(timeGap);
+
+ // this will be cached, previous will be stored
+ mRcsStats.onRcsAcsProvisioningStats(
+ mSubId, responseCode[1], responseType[1], isSingleRegistrationEnabled);
+
+ ArgumentCaptor<RcsAcsProvisioningStats> captor =
+ ArgumentCaptor.forClass(RcsAcsProvisioningStats.class);
+ verify(mPersistAtomsStorage).addRcsAcsProvisioningStats(captor.capture());
+ RcsAcsProvisioningStats stats = captor.getValue();
+ assertEquals(carrierIds[0], stats.carrierId);
+ assertEquals(slotIds[0], stats.slotId);
+ assertEquals(responseCode[0], stats.responseCode);
+ assertEquals(responseType[0], stats.responseType);
+ assertEquals(isSingleRegistrationEnabled, stats.isSingleRegistrationEnabled);
+ assertEquals(timeGap, stats.stateTimerMillis);
+
+ // the last atoms will be cached
+ assertEquals(1, mRcsStats.getRcsAcsProvisioningCachedSize());
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onRcsAcsProvisioningStats_withAtomsInvalidSubId() throws Exception {
+ boolean isSingleRegistrationEnabled = true;
+ int[] responseCode = {200, 401};
+ int[] responseType = {
+ TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML,
+ TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__ERROR};
+ int[] slotIds = {SLOT_ID, SLOT_ID};
+ int[] carrierIds = {CARRIER_ID, CARRIER_ID};
+
+ // this will be cached
+ mRcsStats.onRcsAcsProvisioningStats(
+ mSubId, responseCode[0], responseType[0], isSingleRegistrationEnabled);
+
+ long timeGap = 6000L;
+ mRcsStats.incTimeMillis(timeGap);
+
+ // slotId and carrierId are invalid based on subId
+ mRcsStats.setEnableInvalidSubId();
+
+ // this will not be cached, previous will be stored
+ mRcsStats.onRcsAcsProvisioningStats(
+ mSubId, responseCode[1], responseType[1], isSingleRegistrationEnabled);
+
+ ArgumentCaptor<RcsAcsProvisioningStats> captor =
+ ArgumentCaptor.forClass(RcsAcsProvisioningStats.class);
+ verify(mPersistAtomsStorage).addRcsAcsProvisioningStats(captor.capture());
+ RcsAcsProvisioningStats stats = captor.getValue();
+ assertEquals(carrierIds[0], stats.carrierId);
+ assertEquals(slotIds[0], stats.slotId);
+ assertEquals(responseCode[0], stats.responseCode);
+ assertEquals(responseType[0], stats.responseType);
+ assertEquals(isSingleRegistrationEnabled, stats.isSingleRegistrationEnabled);
+ assertEquals(timeGap, stats.stateTimerMillis);
+ // the last atoms will not be cached
+ assertEquals(0, mRcsStats.getRcsAcsProvisioningCachedSize());
+
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onRcsAcsProvisioningStats_byCallBack() throws Exception {
+ long timeGap = 6000L;
+ boolean isSingleRegistrationEnabled = true;
+ int responseCode = 200;
+ int responseType =
+ TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PRE_PROVISIONING_XML;
+ byte[] config = new byte[0];
+
+ RcsStats.RcsProvisioningCallback rcsProvisioningCallback =
+ mRcsStats.getRcsProvisioningCallback(mSubId, isSingleRegistrationEnabled);
+ // has one callback obj
+ assertEquals(mRcsStats.getRcsProvisioningCallbackMapSize(), 1);
+
+ rcsProvisioningCallback.onPreProvisioningReceived(config);
+ mRcsStats.incTimeMillis(timeGap);
+ rcsProvisioningCallback.onRemoved();
+ // callback will be removed, Map is empty.
+ assertEquals(mRcsStats.getRcsProvisioningCallbackMapSize(), 0);
+
+ ArgumentCaptor<RcsAcsProvisioningStats> captor =
+ ArgumentCaptor.forClass(RcsAcsProvisioningStats.class);
+ verify(mPersistAtomsStorage).addRcsAcsProvisioningStats(captor.capture());
+ RcsAcsProvisioningStats stats = captor.getValue();
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ assertEquals(responseCode, stats.responseCode);
+ assertEquals(responseType, stats.responseType);
+ assertEquals(isSingleRegistrationEnabled, stats.isSingleRegistrationEnabled);
+ assertEquals(timeGap, stats.stateTimerMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onRcsAcsProvisioningStats_byErrorCallBack() throws Exception {
+ long timeGap = 6000L;
+ boolean isSingleRegistrationEnabled = true;
+ int responseCode = 401;
+ int responseType =
+ TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__ERROR;
+
+ RcsStats.RcsProvisioningCallback rcsProvisioningCallback =
+ mRcsStats.getRcsProvisioningCallback(mSubId, false);
+ rcsProvisioningCallback =
+ mRcsStats.getRcsProvisioningCallback(mSubId2, isSingleRegistrationEnabled);
+ // has two callback obj, subId, subId2
+ assertEquals(mRcsStats.getRcsProvisioningCallbackMapSize(), 2);
+
+ rcsProvisioningCallback.onAutoConfigurationErrorReceived(responseCode, "responseCode");
+ mRcsStats.incTimeMillis(timeGap);
+ mRcsStats.onStoreCompleteRcsAcsProvisioningStats(mSubId2);
+ rcsProvisioningCallback.onRemoved();
+ // subId2's callback will be removed, Map has only one callback for subId.
+ assertEquals(mRcsStats.getRcsProvisioningCallbackMapSize(), 1);
+
+ // addRcsAcsProvisioningStats is called once.
+ ArgumentCaptor<RcsAcsProvisioningStats> captor =
+ ArgumentCaptor.forClass(RcsAcsProvisioningStats.class);
+ verify(mPersistAtomsStorage).addRcsAcsProvisioningStats(captor.capture());
+ RcsAcsProvisioningStats stats = captor.getValue();
+ assertEquals(CARRIER2_ID, stats.carrierId);
+ assertEquals(SLOT2_ID, stats.slotId);
+ assertEquals(responseCode, stats.responseCode);
+ assertEquals(responseType, stats.responseType);
+ assertEquals(isSingleRegistrationEnabled, stats.isSingleRegistrationEnabled);
+ assertEquals(timeGap, stats.stateTimerMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onStoreCompleteRcsAcsProvisioningStats_withSubId() throws Exception {
+ boolean isSingleRegistrationEnabled = true;
+ int[] responseCode = {401, 200};
+ /*
+ * RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__ERROR
+ * RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML
+ * RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PRE_PROVISIONING_XML
+ */
+ int[] responseType = {TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__ERROR,
+ TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML};
+ int[] slotIds = {SLOT_ID, SLOT2_ID};
+ int[] carrierIds = {CARRIER_ID, CARRIER2_ID};
+
+ // this will be cached
+ mRcsStats.onRcsAcsProvisioningStats(
+ mSubId, responseCode[0], responseType[0], isSingleRegistrationEnabled);
+ // this will be cached
+ mRcsStats.onRcsAcsProvisioningStats(
+ mSubId2, responseCode[1], responseType[1], isSingleRegistrationEnabled);
+
+ long timeGap = 6000L;
+ mRcsStats.incTimeMillis(timeGap);
+
+ // cached atoms will be stored and removed
+ mRcsStats.onStoreCompleteRcsAcsProvisioningStats(mSubId);
+ mRcsStats.onStoreCompleteRcsAcsProvisioningStats(mSubId2);
+
+ ArgumentCaptor<RcsAcsProvisioningStats> captor =
+ ArgumentCaptor.forClass(RcsAcsProvisioningStats.class);
+ verify(mPersistAtomsStorage, times(slotIds.length))
+ .addRcsAcsProvisioningStats(captor.capture());
+ List<RcsAcsProvisioningStats> statsList = captor.getAllValues();
+ assertEquals(slotIds.length, statsList.size());
+ for (int i = 0; i < statsList.size(); i++) {
+ RcsAcsProvisioningStats stats = statsList.get(i);
+ assertEquals(carrierIds[i], stats.carrierId);
+ assertEquals(slotIds[i], stats.slotId);
+ assertEquals(responseCode[i], stats.responseCode);
+ assertEquals(responseType[i], stats.responseType);
+ assertEquals(isSingleRegistrationEnabled, stats.isSingleRegistrationEnabled);
+ assertEquals(timeGap, stats.stateTimerMillis);
+ }
+ // cached data should be empty
+ assertEquals(0, mRcsStats.getRcsAcsProvisioningCachedSize());
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onFlushIncompleteRcsAcsProvisioningStats_withoutSubId() throws Exception {
+ boolean isSingleRegistrationEnabled = true;
+ int[] responseCode = {401, 200};
+ /*
+ * RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__ERROR
+ * RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML
+ * RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PRE_PROVISIONING_XML
+ */
+ int[] responseType = {TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__ERROR,
+ TelephonyStatsLog.RCS_ACS_PROVISIONING_STATS__RESPONSE_TYPE__PROVISIONING_XML};
+ int[] slotIds = {SLOT_ID, SLOT2_ID};
+ int[] carrierIds = {CARRIER_ID, CARRIER2_ID};
+
+ // this will be cached
+ mRcsStats.onRcsAcsProvisioningStats(
+ mSubId, responseCode[0], responseType[0], isSingleRegistrationEnabled);
+ // this will be cached
+ mRcsStats.onRcsAcsProvisioningStats(
+ mSubId2, responseCode[1], responseType[1], isSingleRegistrationEnabled);
+
+ long timeGap = 6000L;
+ mRcsStats.incTimeMillis(timeGap);
+
+ // cached atoms will be stored, but atoms are keeped
+ mRcsStats.onFlushIncompleteRcsAcsProvisioningStats();
+
+ ArgumentCaptor<RcsAcsProvisioningStats> captor =
+ ArgumentCaptor.forClass(RcsAcsProvisioningStats.class);
+ verify(mPersistAtomsStorage, times(slotIds.length))
+ .addRcsAcsProvisioningStats(captor.capture());
+ List<RcsAcsProvisioningStats> statsList = captor.getAllValues();
+ assertEquals(slotIds.length, statsList.size());
+ for (int i = 0; i < statsList.size(); i++) {
+ RcsAcsProvisioningStats stats = statsList.get(i);
+ assertEquals(carrierIds[i], stats.carrierId);
+ assertEquals(slotIds[i], stats.slotId);
+ assertEquals(responseCode[i], stats.responseCode);
+ assertEquals(responseType[i], stats.responseType);
+ assertEquals(isSingleRegistrationEnabled, stats.isSingleRegistrationEnabled);
+ assertEquals(timeGap, stats.stateTimerMillis);
+
+ // check cached atom's time should be updated
+ assertEquals(mRcsStats.getWallTimeMillis(),
+ mRcsStats.getRcsAcsProvisioningCachedTime(carrierIds[i], slotIds[i]));
+ }
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onSipDelegateStats_addStats() throws Exception {
+ final int destroyReason = SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD;
+ final long timeGap = 6000L;
+ List<Set<String>> supportedTagsList = getSupportedTagsList();
+ Set<String> registeredTags = supportedTagsList.get(0);
+ // create and destroy a sipDelegate..
+ mRcsStats.createSipDelegateStats(mSubId, registeredTags);
+ mRcsStats.incTimeMillis(timeGap);
+ mRcsStats.onSipDelegateStats(mSubId, registeredTags,
+ SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+
+ ArgumentCaptor<SipDelegateStats> captor =
+ ArgumentCaptor.forClass(SipDelegateStats.class);
+ verify(mPersistAtomsStorage).addSipDelegateStats(captor.capture());
+ SipDelegateStats stats = captor.getValue();
+ assertTrue(stats.dimension != 0);
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ assertEquals(timeGap, stats.uptimeMillis);
+ assertEquals(destroyReason, stats.destroyReason);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ private List<Set<String>> getSupportedTagsList() {
+ List<Set<String>> registeredTagsList = new ArrayList<>();
+ Set<String> supportedTags1 = new ArraySet<>();
+ supportedTags1.add(FeatureTags.FEATURE_TAG_STANDALONE_MSG);
+ supportedTags1.add(FeatureTags.FEATURE_TAG_CHAT_SESSION);
+ registeredTagsList.add(supportedTags1);
+
+ Set<String> supportedTags2 = new ArraySet<>();
+ supportedTags2.add(FeatureTags.FEATURE_TAG_FILE_TRANSFER);
+ supportedTags2.add(FeatureTags.FEATURE_TAG_CHAT_IM);
+ supportedTags2.add(FeatureTags.FEATURE_TAG_CALL_COMPOSER_ENRICHED_CALLING);
+ registeredTagsList.add(supportedTags2);
+
+ Set<String> supportedTags3 = new ArraySet<>();
+ supportedTags3.add(FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_SESSION);
+ supportedTags3.add(FeatureTags.FEATURE_TAG_CHATBOT_COMMUNICATION_USING_STANDALONE_MSG);
+ supportedTags3.add(FeatureTags.FEATURE_TAG_CHATBOT_VERSION_SUPPORTED);
+ registeredTagsList.add(supportedTags3);
+
+ return registeredTagsList;
+ }
+
+ @Test
+ @SmallTest
+ public void onSipDelegateStats_addMultipleEntries() throws Exception {
+ final long timeGap = 6000L;
+ List<Integer> destroyReasonList = new ArrayList<>();
+ destroyReasonList.add(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_UNKNOWN);
+ destroyReasonList.add(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD);
+ destroyReasonList.add(SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP);
+ final int testSize = destroyReasonList.size();
+ List<Set<String>> supportedTagsList = getSupportedTagsList();
+
+ // create and destroy a sipDelegate multiple times
+ for (int i = 0; i < testSize; i++) {
+ mRcsStats.createSipDelegateStats(mSubId, supportedTagsList.get(i));
+ }
+
+ for (int i = 0; i < testSize; i++) {
+ mRcsStats.incTimeMillis(timeGap);
+ mRcsStats.onSipDelegateStats(mSubId, supportedTagsList.get(i),
+ destroyReasonList.get(i));
+ }
+
+ List<ExpectedSipDelegateResult> expectedSipDelegateResults =
+ getExpectedResult(destroyReasonList);
+ final int expectedResultSize = expectedSipDelegateResults.size();
+ ArgumentCaptor<SipDelegateStats> captor =
+ ArgumentCaptor.forClass(SipDelegateStats.class);
+ verify(mPersistAtomsStorage, times(expectedResultSize))
+ .addSipDelegateStats(captor.capture());
+
+ List<SipDelegateStats> captorValues = captor.getAllValues();
+ assertEquals(captorValues.size(), expectedResultSize);
+ for (int i = 0; i < expectedResultSize; i++) {
+ SipDelegateStats stats = captorValues.get(i);
+ ExpectedSipDelegateResult expectedResult = expectedSipDelegateResults.get(i);
+ assertTrue(stats.dimension != 0);
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ assertEquals(timeGap * (i + 1), stats.uptimeMillis);
+ assertEquals(expectedResult.destroyReason, stats.destroyReason);
+ }
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ private class ExpectedSipDelegateResult {
+ public int id;
+ public int destroyReason;
+ ExpectedSipDelegateResult(int id, int destroyReason) {
+ this.id = id;
+ this.destroyReason = destroyReason;
+ }
+ }
+
+ private List<ExpectedSipDelegateResult> getExpectedResult(List<Integer> destroyReasonList) {
+ List<ExpectedSipDelegateResult> results = new ArrayList<>();
+ int size = destroyReasonList.size();
+
+ for (int i = 0; i < size; i++) {
+ results.add(new ExpectedSipDelegateResult(i, destroyReasonList.get(i)));
+ }
+
+ return results;
+ }
+
+ @Test
+ @SmallTest
+ public void onSipTransportFeatureTagStats_addMultipleEntries() throws Exception {
+ final long timeGap = 6000L;
+ Set<FeatureTagState> deniedTags = new ArraySet<>();
+ Set<FeatureTagState> deRegiTags = new ArraySet<>();
+ Set<String> regiTags = new ArraySet<>();
+
+ // create new featureTags
+ regiTags.add(FeatureTags.FEATURE_TAG_STANDALONE_MSG);
+ deniedTags.add(new FeatureTagState(FeatureTags.FEATURE_TAG_FILE_TRANSFER,
+ SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE));
+ mRcsStats.onSipTransportFeatureTagStats(mSubId, deniedTags, deRegiTags, regiTags);
+
+ mRcsStats.incTimeMillis(timeGap);
+
+ // change status of featureTags
+ regiTags.clear();
+ deRegiTags.add(new FeatureTagState(FeatureTags.FEATURE_TAG_STANDALONE_MSG,
+ DelegateRegistrationState.DEREGISTERED_REASON_NOT_REGISTERED));
+ mRcsStats.onSipTransportFeatureTagStats(mSubId, deniedTags, deRegiTags, regiTags);
+
+ mRcsStats.incTimeMillis(timeGap);
+
+ List<TestResult> expectedResults = getTestResult(timeGap, false);
+
+ int expectedResultSize = expectedResults.size();
+ ArgumentCaptor<SipTransportFeatureTagStats> captor =
+ ArgumentCaptor.forClass(SipTransportFeatureTagStats.class);
+ verify(mPersistAtomsStorage, times(expectedResultSize))
+ .addSipTransportFeatureTagStats(captor.capture());
+
+ List<SipTransportFeatureTagStats> captorValues = captor.getAllValues();
+
+ assertEquals(captorValues.size(), expectedResultSize);
+ for (int i = 0; i < captorValues.size(); i++) {
+ SipTransportFeatureTagStats stats = captorValues.get(i);
+ TestResult expectedResult = expectedResults.get(i);
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ assertEquals(expectedResult.tagValue, stats.featureTagName);
+ assertEquals(expectedResult.duration, stats.associatedMillis);
+ assertEquals(expectedResult.deniedReason, stats.sipTransportDeniedReason);
+ assertEquals(expectedResult.deregiReason, stats.sipTransportDeregisteredReason);
+ }
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onSipTransportFeatureTagStats_addInvalidEntries() throws Exception {
+ final long timeGap = 6000L;
+ Set<FeatureTagState> deniedTags = new ArraySet<>();
+ Set<FeatureTagState> deRegiTags = new ArraySet<>();
+ Set<String> regiTags = new ArraySet<>();
+
+ final int invalidSubId = INVALID_SUB_ID;
+
+ // create new featureTags with an invalidId
+ regiTags.add(FeatureTags.FEATURE_TAG_STANDALONE_MSG);
+ deniedTags.add(new FeatureTagState(FeatureTags.FEATURE_TAG_FILE_TRANSFER,
+ SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE));
+ mRcsStats.onSipTransportFeatureTagStats(invalidSubId, deniedTags, deRegiTags, regiTags);
+ mRcsStats.incTimeMillis(timeGap);
+
+ // change status of featureTags with an invalidId
+ regiTags.clear();
+ deRegiTags.add(new FeatureTagState(FeatureTags.FEATURE_TAG_STANDALONE_MSG,
+ DelegateRegistrationState.DEREGISTERED_REASON_NOT_REGISTERED));
+ mRcsStats.onSipTransportFeatureTagStats(invalidSubId, deniedTags, deRegiTags, regiTags);
+ mRcsStats.incTimeMillis(timeGap);
+
+ verify(mPersistAtomsStorage, never()).addSipTransportFeatureTagStats(any());
+ }
+
+
+ @Test
+ @SmallTest
+ public void onSipTransportFeatureTagStats_addCustomTag() throws Exception {
+ final long timeGap = 6000L;
+ Set<FeatureTagState> deniedTags = new ArraySet<>();
+ Set<FeatureTagState> deRegiTags = new ArraySet<>();
+ Set<String> regiTags = new ArraySet<>();
+
+ // create new featureTags
+ String customTag = "custom@tag";
+ regiTags.add(customTag);
+ mRcsStats.onSipTransportFeatureTagStats(mSubId, deniedTags, deRegiTags, regiTags);
+
+ mRcsStats.incTimeMillis(timeGap);
+
+ // change status of featureTags
+ regiTags.clear();
+ deRegiTags.add(new FeatureTagState(customTag,
+ DelegateRegistrationState.DEREGISTERED_REASON_NOT_REGISTERED));
+ mRcsStats.onSipTransportFeatureTagStats(mSubId, deniedTags, deRegiTags, regiTags);
+
+ mRcsStats.incTimeMillis(timeGap);
+
+ TestResult expectedResult = new TestResult(customTag,
+ TelephonyProtoEnums.IMS_FEATURE_TAG_CUSTOM, timeGap, RcsStats.NONE, RcsStats.NONE);
+
+ ArgumentCaptor<SipTransportFeatureTagStats> captor =
+ ArgumentCaptor.forClass(SipTransportFeatureTagStats.class);
+
+ verify(mPersistAtomsStorage).addSipTransportFeatureTagStats(captor.capture());
+ SipTransportFeatureTagStats stats = captor.getValue();
+
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ assertEquals(expectedResult.tagValue, stats.featureTagName);
+ assertEquals(expectedResult.duration, stats.associatedMillis);
+ assertEquals(expectedResult.deniedReason, stats.sipTransportDeniedReason);
+ assertEquals(expectedResult.deregiReason, stats.sipTransportDeregisteredReason);
+
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void concludeSipTransportFeatureTagsStat_addMultipleEntries() throws Exception {
+ final long timeGap = 6000L;
+ Set<FeatureTagState> deniedTags = new ArraySet<>();
+ Set<FeatureTagState> deRegiTags = new ArraySet<>();
+ Set<String> regiTags = new ArraySet<>();
+ // create new featureTags
+ regiTags.add(FeatureTags.FEATURE_TAG_STANDALONE_MSG);
+ deniedTags.add(new FeatureTagState(FeatureTags.FEATURE_TAG_FILE_TRANSFER,
+ SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE));
+ mRcsStats.onSipTransportFeatureTagStats(mSubId, deniedTags, deRegiTags, regiTags);
+
+ mRcsStats.incTimeMillis(timeGap);
+
+ // change status of featureTags
+ regiTags.clear();
+ deRegiTags.add(new FeatureTagState(FeatureTags.FEATURE_TAG_STANDALONE_MSG,
+ DelegateRegistrationState.DEREGISTERED_REASON_NOT_REGISTERED));
+ mRcsStats.onSipTransportFeatureTagStats(mSubId, deniedTags, deRegiTags, regiTags);
+
+
+ mRcsStats.incTimeMillis(timeGap);
+
+ // change status of featureTags and metrics are pulled.
+ deRegiTags.clear();
+ regiTags.add(FeatureTags.FEATURE_TAG_STANDALONE_MSG);
+ mRcsStats.onSipTransportFeatureTagStats(mSubId, deniedTags, deRegiTags, regiTags);
+
+ mRcsStats.incTimeMillis(timeGap);
+ mRcsStats.concludeSipTransportFeatureTagsStat();
+
+ List<TestResult> expectedResults = getTestResult(timeGap, true);
+
+ int expectedResultSize = expectedResults.size();
+ ArgumentCaptor<SipTransportFeatureTagStats> captor =
+ ArgumentCaptor.forClass(SipTransportFeatureTagStats.class);
+ verify(mPersistAtomsStorage, times(expectedResultSize))
+ .addSipTransportFeatureTagStats(captor.capture());
+
+ List<SipTransportFeatureTagStats> captorValues = captor.getAllValues();
+
+ assertEquals(captorValues.size(), expectedResultSize);
+ for (int i = 0; i < captorValues.size(); i++) {
+ SipTransportFeatureTagStats stats = captorValues.get(i);
+ TestResult expectedResult = expectedResults.get(i);
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ assertEquals(expectedResult.tagValue, stats.featureTagName);
+ assertEquals(expectedResult.duration, stats.associatedMillis);
+ assertEquals(expectedResult.deniedReason, stats.sipTransportDeniedReason);
+ assertEquals(expectedResult.deregiReason, stats.sipTransportDeregisteredReason);
+ }
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+
+ }
+
+ private List<TestResult> getTestResult(long timeGap, boolean concludeTest) {
+ List<TestResult> results = new ArrayList<>();
+ results.add(new TestResult(FeatureTags.FEATURE_TAG_FILE_TRANSFER,
+ TelephonyProtoEnums.IMS_FEATURE_TAG_FILE_TRANSFER,
+ timeGap,
+ SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE, RcsStats.NONE));
+ results.add(new TestResult(FeatureTags.FEATURE_TAG_STANDALONE_MSG,
+ TelephonyProtoEnums.IMS_FEATURE_TAG_STANDALONE_MSG,
+ timeGap, RcsStats.NONE, RcsStats.NONE));
+ if (concludeTest) {
+ results.add(new TestResult(FeatureTags.FEATURE_TAG_STANDALONE_MSG,
+ TelephonyProtoEnums.IMS_FEATURE_TAG_STANDALONE_MSG,
+ timeGap, RcsStats.NONE,
+ DelegateRegistrationState.DEREGISTERED_REASON_NOT_REGISTERED));
+ results.add(new TestResult(FeatureTags.FEATURE_TAG_FILE_TRANSFER,
+ TelephonyProtoEnums.IMS_FEATURE_TAG_FILE_TRANSFER,
+ timeGap,
+ SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE, RcsStats.NONE));
+ results.add(new TestResult(FeatureTags.FEATURE_TAG_FILE_TRANSFER,
+ TelephonyProtoEnums.IMS_FEATURE_TAG_FILE_TRANSFER,
+ timeGap,
+ SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE, RcsStats.NONE));
+ results.add(new TestResult(FeatureTags.FEATURE_TAG_STANDALONE_MSG,
+ TelephonyProtoEnums.IMS_FEATURE_TAG_STANDALONE_MSG,
+ timeGap, RcsStats.NONE, RcsStats.NONE));
+
+ }
+ return results;
+ }
+
+ @Test
+ @SmallTest
+ public void onSipMessageResponse_withAtoms() throws Exception {
+ String testSipMessageMethod = "MESSAGE";
+ int testSipRequestMessageDirection = 1; //INCOMING: 0, OUTGOING: 1
+ int testSipMessageResponse = 200;
+ int testMessageError = 0;
+ String testCallId = "testId";
+ // Request message
+ mRcsStats.onSipMessageRequest(testCallId, testSipMessageMethod,
+ testSipRequestMessageDirection);
+ // Response message
+ mRcsStats.onSipMessageResponse(mSubId, testCallId, testSipMessageResponse,
+ testMessageError);
+ ArgumentCaptor<SipMessageResponse> captor =
+ ArgumentCaptor.forClass(SipMessageResponse.class);
+ verify(mPersistAtomsStorage).addSipMessageResponse(captor.capture());
+ SipMessageResponse stats = captor.getValue();
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ assertEquals(TelephonyProtoEnums.SIP_REQUEST_MESSAGE, stats.sipMessageMethod);
+ assertEquals(testSipRequestMessageDirection, stats.sipMessageDirection);
+ assertEquals(testSipMessageResponse, stats.sipMessageResponse);
+ assertEquals(testMessageError, stats.messageError);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onSipTransportSession_withAtoms() throws Exception {
+ String testInviteSipMethod = "INVITE";
+ String testCallId = "testId";
+ int testSipResponse = 0;
+ int testSipRequestMessageDirection = 1; //INCOMING: 0, OUTGOING: 1
+ // Request Message
+ mRcsStats.earlySipTransportSession(
+ testInviteSipMethod, testCallId, testSipRequestMessageDirection);
+ // gracefully close
+ mRcsStats.onSipTransportSessionClosed(mSubId, testCallId, testSipResponse, true);
+ ArgumentCaptor<SipTransportSession> captor =
+ ArgumentCaptor.forClass(SipTransportSession.class);
+ verify(mPersistAtomsStorage).addCompleteSipTransportSession(captor.capture());
+ SipTransportSession stats = captor.getValue();
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ assertEquals(TelephonyProtoEnums.SIP_REQUEST_INVITE, stats.sessionMethod);
+ assertEquals(testSipRequestMessageDirection, stats.sipMessageDirection);
+ assertEquals(testSipResponse, stats.sipResponse);
+ assertEquals(true/*isEndedGracefully*/, stats.isEndedGracefully);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsDedicatedBearerListenerEvent_Added() throws Exception {
+ final int listenerId = 1;
+ int ratAtEnd = TelephonyProtoEnums.NETWORK_TYPE_LTE;
+ final int qci = 5;
+
+ mRcsStats.dedicatedBearerListenerEventMap_remove(listenerId);
+ mRcsStats.onImsDedicatedBearerListenerAdded(listenerId, SLOT_ID, ratAtEnd, qci);
+ assertTrue(mRcsStats.dedicatedBearerListenerEventMap_containsKey(listenerId));
+ ImsDedicatedBearerListenerEvent testProto =
+ mRcsStats.dedicatedBearerListenerEventMap_get(listenerId);
+ assertEquals(SLOT_ID, testProto.slotId);
+ assertEquals(ratAtEnd, testProto.ratAtEnd);
+ assertEquals(qci, testProto.qci);
+ assertFalse(testProto.dedicatedBearerEstablished);
+ verify(mPersistAtomsStorage, never()).addImsDedicatedBearerListenerEvent(any());
+
+ // same listenerId, different contents. should be ignored
+ ratAtEnd = TelephonyProtoEnums.NETWORK_TYPE_NR;
+ mRcsStats.onImsDedicatedBearerListenerAdded(listenerId, SLOT_ID + 1, ratAtEnd + 1, qci + 1);
+ testProto = mRcsStats.dedicatedBearerListenerEventMap_get(listenerId);
+ assertEquals(SLOT_ID, testProto.slotId);
+ assertNotEquals(ratAtEnd, testProto.ratAtEnd);
+ assertEquals(qci, testProto.qci);
+ verify(mPersistAtomsStorage, never()).addImsDedicatedBearerListenerEvent(any());
+
+ mRcsStats.dedicatedBearerListenerEventMap_remove(listenerId);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsDedicatedBearerListenerEvent_bearerEstablished() throws Exception {
+ final int listenerId = 2;
+ final int rat = TelephonyProtoEnums.NETWORK_TYPE_LTE;
+ final int qci = 6;
+
+ mRcsStats.dedicatedBearerListenerEventMap_remove(listenerId);
+ mRcsStats.onImsDedicatedBearerListenerUpdateSession(listenerId, SLOT_ID, rat, qci, true);
+ verify(mPersistAtomsStorage, never()).addImsDedicatedBearerListenerEvent(any());
+
+ mRcsStats.dedicatedBearerListenerEventMap_remove(listenerId);
+ mRcsStats.onImsDedicatedBearerListenerAdded(listenerId, SLOT_ID, rat, qci);
+ assertTrue(mRcsStats.dedicatedBearerListenerEventMap_containsKey(listenerId));
+ mRcsStats.onImsDedicatedBearerListenerUpdateSession(listenerId, SLOT_ID, rat, qci, true);
+ ImsDedicatedBearerListenerEvent testProto =
+ mRcsStats.dedicatedBearerListenerEventMap_get(listenerId);
+ assertEquals(qci, testProto.qci);
+ assertTrue(testProto.dedicatedBearerEstablished);
+
+ verify(mPersistAtomsStorage, never()).addImsDedicatedBearerListenerEvent(any());
+ }
+
+ @Test
+ @SmallTest
+ public void onImsDedicatedBearerListenerEvent_Removed() throws Exception {
+ final int listenerId = 3;
+ final int rat = TelephonyProtoEnums.NETWORK_TYPE_LTE;
+ final int qci = 7;
+
+ mRcsStats.dedicatedBearerListenerEventMap_remove(listenerId);
+ mRcsStats.onImsDedicatedBearerListenerRemoved(listenerId);
+ verify(mPersistAtomsStorage, never()).addImsDedicatedBearerListenerEvent(any());
+
+ mRcsStats.onImsDedicatedBearerListenerAdded(listenerId, SLOT_ID, rat, qci);
+ mRcsStats.onImsDedicatedBearerListenerUpdateSession(listenerId, SLOT_ID, rat, qci, true);
+ mRcsStats.onImsDedicatedBearerListenerRemoved(listenerId);
+ verify(mPersistAtomsStorage, times(1)).addImsDedicatedBearerListenerEvent(any());
+
+ // and values should be same
+ ArgumentCaptor<ImsDedicatedBearerListenerEvent> captor =
+ ArgumentCaptor.forClass(ImsDedicatedBearerListenerEvent.class);
+ verify(mPersistAtomsStorage).addImsDedicatedBearerListenerEvent(captor.capture());
+ ImsDedicatedBearerListenerEvent stats = captor.getValue();
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ assertEquals(rat, stats.ratAtEnd);
+ assertEquals(qci, stats.qci);
+ assertEquals(true, stats.dedicatedBearerEstablished);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+
+ assertFalse(mRcsStats.dedicatedBearerListenerEventMap_containsKey(listenerId));
+ }
+
+ @Test
+ @SmallTest
+ public void onImsDedicatedBearerEvent_withAtoms() throws Exception {
+ // reference comments in test_imsDedicatedBearerListenerEvent for canditate value
+ int ratAtEnd = TelephonyStatsLog
+ .IMS_DEDICATED_BEARER_LISTENER_EVENT__RAT_AT_END__NETWORK_TYPE_LTE_CA;
+ int qci = 6;
+ /*
+ * IMS_DEDICATED_BEARER_EVENT__BEARER_STATE__STATE_ADDED = 1;
+ * IMS_DEDICATED_BEARER_EVENT__BEARER_STATE__STATE_MODIFIED = 2;
+ * IMS_DEDICATED_BEARER_EVENT__BEARER_STATE__STATE_DELETED = 3;
+ */
+ int bearerState = TelephonyStatsLog.IMS_DEDICATED_BEARER_EVENT__BEARER_STATE__STATE_ADDED;
+ boolean localConnectionInfoReceived = false;
+ boolean remoteConnectionInfoReceived = true;
+ boolean hasListeners = true;
+
+ mRcsStats.onImsDedicatedBearerEvent(SLOT_ID, ratAtEnd, qci, bearerState,
+ localConnectionInfoReceived, remoteConnectionInfoReceived, hasListeners);
+
+ ArgumentCaptor<ImsDedicatedBearerEvent> captor =
+ ArgumentCaptor.forClass(ImsDedicatedBearerEvent.class);
+ verify(mPersistAtomsStorage).addImsDedicatedBearerEvent(captor.capture());
+ ImsDedicatedBearerEvent stats = captor.getValue();
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ assertEquals(ratAtEnd, stats.ratAtEnd);
+ assertEquals(qci, stats.qci);
+ assertEquals(bearerState, stats.bearerState);
+ assertEquals(localConnectionInfoReceived, stats.localConnectionInfoReceived);
+ assertEquals(remoteConnectionInfoReceived, stats.remoteConnectionInfoReceived);
+ assertEquals(hasListeners, stats.hasListeners);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsRegistrationServiceDescStats_withAtoms() throws Exception {
+ int registrationTech = 0; //ImsRegistrationImplBase.REGISTRATION_TECH_LTE
+ ArrayList<String> serviceIdList = new ArrayList<>();
+ serviceIdList.add("org.openmobilealliance:File-Transfer-HTTP");
+ serviceIdList.add("org.openmobilealliance:IM-session");
+ serviceIdList.add("Unknown1");
+ ArrayList<String> serviceIdVersionList = new ArrayList<>();
+ serviceIdVersionList.add("1.0");
+ serviceIdVersionList.add("1.0");
+ serviceIdVersionList.add("3.0");
+
+ mRcsStats.onImsRegistrationServiceDescStats(mSubId, serviceIdList, serviceIdVersionList,
+ registrationTech);
+
+ // getWallTimeMillis
+ /*
+ * UCE_EVENT__TYPE__PUBLISH = 0;
+ * UCE_EVENT__TYPE__SUBSCRIBE = 1;
+ * UCE_EVENT__TYPE__INCOMING_OPTION = 2;
+ * UCE_EVENT__TYPE__OUTGOING_OPTION = 3;
+ */
+ int type = TelephonyStatsLog.UCE_EVENT_STATS__TYPE__PUBLISH;
+ boolean successful = true;
+ /*
+ * UCE_EVENT__COMMAND_CODE__SERVICE_UNKNOWN = 0;
+ * UCE_EVENT__COMMAND_CODE__GENERIC_FAILURE = 1;
+ * UCE_EVENT__COMMAND_CODE__INVALID_PARAM = 2;
+ * UCE_EVENT__COMMAND_CODE__FETCH_ERROR = 3;
+ * UCE_EVENT__COMMAND_CODE__REQUEST_TIMEOUT = 4;
+ * UCE_EVENT__COMMAND_CODE__INSUFFICIENT_MEMORY = 5;
+ * UCE_EVENT__COMMAND_CODE__LOST_NETWORK_CONNECTION = 6;
+ * UCE_EVENT__COMMAND_CODE__NOT_SUPPORTED = 7;
+ * UCE_EVENT__COMMAND_CODE__NOT_FOUND = 8;
+ * UCE_EVENT__COMMAND_CODE__SERVICE_UNAVAILABLE = 9;
+ * UCE_EVENT__COMMAND_CODE__NO_CHANGE = 10;
+ */
+ int commandCode = TelephonyStatsLog.UCE_EVENT_STATS__COMMAND_CODE__SERVICE_UNAVAILABLE;
+ int networkResponse = 200;
+
+ mRcsStats.onUceEventStats(mSubId, type, successful, commandCode, networkResponse);
+
+ {
+ ArgumentCaptor<UceEventStats> captor = ArgumentCaptor.forClass(UceEventStats.class);
+ verify(mPersistAtomsStorage).addUceEventStats(captor.capture());
+ UceEventStats stats = captor.getValue();
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ assertEquals(successful, stats.successful);
+ assertEquals(commandCode, stats.commandCode);
+ assertEquals(networkResponse, stats.networkResponse);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ long timeGap = 6000L;
+ mRcsStats.incTimeMillis(timeGap);
+
+ mRcsStats.onStoreCompleteImsRegistrationServiceDescStats(mSubId);
+
+ ArgumentCaptor<ImsRegistrationServiceDescStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationServiceDescStats.class);
+ verify(mPersistAtomsStorage, times(3))
+ .addImsRegistrationServiceDescStats(captor.capture());
+ List<ImsRegistrationServiceDescStats> captorValues = captor.getAllValues();
+
+ assertEquals(captorValues.size(), serviceIdList.size());
+
+ for (int index = 0; index < captorValues.size(); index++) {
+ ImsRegistrationServiceDescStats stats = captorValues.get(index);
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ int serviceId = mRcsStats.convertServiceIdToValue(serviceIdList.get(index));
+ assertEquals(serviceId, stats.serviceIdName);
+ float serviceVersionFloat = Float.parseFloat(serviceIdVersionList.get(index));
+ assertEquals(serviceVersionFloat, stats.serviceIdVersion, 0.1f);
+ assertEquals(registrationTech, stats.registrationTech);
+ assertEquals(timeGap, stats.publishedMillis);
+ }
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsRegistrationServiceDescStats_withAtomsInvalidSubId() throws Exception {
+ int registrationTech = 0; //ImsRegistrationImplBase.REGISTRATION_TECH_LTE
+ ArrayList<String> serviceIdList = new ArrayList<>();
+ serviceIdList.add("org.openmobilealliance:File-Transfer-HTTP");
+ serviceIdList.add("org.openmobilealliance:IM-session");
+ serviceIdList.add("Unknown1");
+ ArrayList<String> serviceIdVersionList = new ArrayList<>();
+ serviceIdVersionList.add("1.0");
+ serviceIdVersionList.add("1.0");
+ serviceIdVersionList.add("3.0");
+
+ mRcsStats.onImsRegistrationServiceDescStats(mSubId, serviceIdList, serviceIdVersionList,
+ registrationTech);
+
+ // getWallTimeMillis
+ /*
+ * UCE_EVENT__TYPE__PUBLISH = 0;
+ * UCE_EVENT__TYPE__SUBSCRIBE = 1;
+ * UCE_EVENT__TYPE__INCOMING_OPTION = 2;
+ * UCE_EVENT__TYPE__OUTGOING_OPTION = 3;
+ */
+ int type = TelephonyStatsLog.UCE_EVENT_STATS__TYPE__PUBLISH;
+ boolean successful = true;
+ /*
+ * UCE_EVENT__COMMAND_CODE__SERVICE_UNKNOWN = 0;
+ * UCE_EVENT__COMMAND_CODE__GENERIC_FAILURE = 1;
+ * UCE_EVENT__COMMAND_CODE__INVALID_PARAM = 2;
+ * UCE_EVENT__COMMAND_CODE__FETCH_ERROR = 3;
+ * UCE_EVENT__COMMAND_CODE__REQUEST_TIMEOUT = 4;
+ * UCE_EVENT__COMMAND_CODE__INSUFFICIENT_MEMORY = 5;
+ * UCE_EVENT__COMMAND_CODE__LOST_NETWORK_CONNECTION = 6;
+ * UCE_EVENT__COMMAND_CODE__NOT_SUPPORTED = 7;
+ * UCE_EVENT__COMMAND_CODE__NOT_FOUND = 8;
+ * UCE_EVENT__COMMAND_CODE__SERVICE_UNAVAILABLE = 9;
+ * UCE_EVENT__COMMAND_CODE__NO_CHANGE = 10;
+ */
+ int commandCode = TelephonyStatsLog.UCE_EVENT_STATS__COMMAND_CODE__SERVICE_UNAVAILABLE;
+ int networkResponse = 200;
+ mRcsStats.onUceEventStats(mSubId, type, successful, commandCode, networkResponse);
+
+ // slotId and carrierId are invalid based on subId
+ mRcsStats.setEnableInvalidSubId();
+ long timeGap = 6000L;
+ mRcsStats.incTimeMillis(timeGap);
+ mRcsStats.onUceEventStats(mSubId, type, successful, commandCode, networkResponse);
+
+ ArgumentCaptor<ImsRegistrationServiceDescStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationServiceDescStats.class);
+ verify(mPersistAtomsStorage, times(3))
+ .addImsRegistrationServiceDescStats(captor.capture());
+ List<ImsRegistrationServiceDescStats> captorValues = captor.getAllValues();
+
+ assertEquals(captorValues.size(), serviceIdList.size());
+
+ for (int index = 0; index < captorValues.size(); index++) {
+ ImsRegistrationServiceDescStats stats = captorValues.get(index);
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ int serviceId = mRcsStats.convertServiceIdToValue(serviceIdList.get(index));
+ assertEquals(serviceId, stats.serviceIdName);
+ float serviceVersionFloat = Float.parseFloat(serviceIdVersionList.get(index));
+ assertEquals(serviceVersionFloat, stats.serviceIdVersion, 0.1f);
+ assertEquals(registrationTech, stats.registrationTech);
+ assertEquals(timeGap, stats.publishedMillis);
+ }
+ assertEquals(0, mRcsStats.getImsRegistrationServiceDescCachedSize());
+ }
+
+ @Test
+ @SmallTest
+ public void onUceEventStats_withAtoms() throws Exception {
+ int messageType = TelephonyStatsLog.UCE_EVENT_STATS__TYPE__PUBLISH;
+ boolean successful = true;
+ int commandCode = TelephonyStatsLog.UCE_EVENT_STATS__COMMAND_CODE__REQUEST_TIMEOUT;
+ int networkResponse = 408;
+
+ mRcsStats.onUceEventStats(mSubId, messageType, successful, commandCode, networkResponse);
+
+ ArgumentCaptor<UceEventStats> captor = ArgumentCaptor.forClass(UceEventStats.class);
+ verify(mPersistAtomsStorage).addUceEventStats(captor.capture());
+ UceEventStats stats = captor.getValue();
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ assertEquals(successful, stats.successful);
+ assertEquals(commandCode, stats.commandCode);
+ assertEquals(networkResponse, stats.networkResponse);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onPresenceNotifyEvent_withAtoms() throws Exception {
+ String reason = "deactivated";
+ boolean contentBodyReceived = true;
+ boolean rcsCaps = true;
+ boolean mmtelCaps = false;
+ boolean noCaps = false;
+
+ mRcsStats.onPresenceNotifyEvent(mSubId, reason, contentBodyReceived,
+ rcsCaps, mmtelCaps, noCaps);
+
+ ArgumentCaptor<PresenceNotifyEvent> captor =
+ ArgumentCaptor.forClass(PresenceNotifyEvent.class);
+ verify(mPersistAtomsStorage).addPresenceNotifyEvent(captor.capture());
+ PresenceNotifyEvent stats = captor.getValue();
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ int reasonInt = mRcsStats.convertPresenceNotifyReason(reason);
+ assertEquals(reasonInt, stats.reason);
+ assertEquals(contentBodyReceived, stats.contentBodyReceived);
+ assertEquals(1, stats.rcsCapsCount);
+ assertEquals(0, stats.mmtelCapsCount);
+ assertEquals(0, stats.noCapsCount);
+ assertEquals(1, stats.rcsCapsCount);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onGbaEvent_withAtoms() throws Exception {
+ boolean successful = false;
+ /*
+ * GBA_EVENT__FAILED_REASON__UNKNOWN
+ * GBA_EVENT__FAILED_REASON__FEATURE_NOT_SUPPORTED
+ * GBA_EVENT__FAILED_REASON__FEATURE_NOT_READY
+ * GBA_EVENT__FAILED_REASON__NETWORK_FAILURE
+ * GBA_EVENT__FAILED_REASON__INCORRECT_NAF_ID
+ * GBA_EVENT__FAILED_REASON__SECURITY_PROTOCOL_NOT_SUPPORTED
+ */
+ int failedReason = TelephonyStatsLog.GBA_EVENT__FAILED_REASON__FEATURE_NOT_READY;
+
+ mRcsStats.onGbaFailureEvent(mSubId, failedReason);
+
+ ArgumentCaptor<GbaEvent> captor = ArgumentCaptor.forClass(GbaEvent.class);
+ verify(mPersistAtomsStorage).addGbaEvent(captor.capture());
+ GbaEvent stats = captor.getValue();
+ assertEquals(CARRIER_ID, stats.carrierId);
+ assertEquals(SLOT_ID, stats.slotId);
+ assertEquals(successful, stats.successful);
+ assertEquals(failedReason, stats.failedReason);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+}