Merge "[NS01.tl] Remove an unused argument"
diff --git a/proto/src/persist_atoms.proto b/proto/src/persist_atoms.proto
index 2f84639..d7cc58f 100644
--- a/proto/src/persist_atoms.proto
+++ b/proto/src/persist_atoms.proto
@@ -23,19 +23,67 @@
// 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: 5
+// Next id: 21
message PersistAtoms {
/* Aggregated RAT usage during the call. */
- repeated RawVoiceCallRatUsage raw_voice_call_rat_usage = 1;
+ repeated VoiceCallRatUsage voice_call_rat_usage = 1;
/* Timestamp of last voice_call_rat_usages pull. */
- optional int64 raw_voice_call_rat_usage_pull_timestamp_millis = 2;
+ optional int64 voice_call_rat_usage_pull_timestamp_millis = 2;
/* Per call statistics and information. */
repeated VoiceCallSession voice_call_session = 3;
/* Timestamp of last voice_call_sessions pull. */
optional int64 voice_call_session_pull_timestamp_millis = 4;
+
+ /* Incoming SMS statistics and information. */
+ repeated IncomingSms incoming_sms = 5;
+
+ /* Timestamp of last incoming_sms pull. */
+ optional int64 incoming_sms_pull_timestamp_millis = 6;
+
+ /* Outgoing SMS statistics and information. */
+ repeated OutgoingSms outgoing_sms = 7;
+
+ /* Timestamp of last incoming_sms pull. */
+ optional int64 outgoing_sms_pull_timestamp_millis = 8;
+
+ /* List of carrier ID mismatch events already sent. */
+ repeated CarrierIdMismatch carrier_id_mismatch = 9;
+
+ /* Last version of carrier ID table sent. */
+ optional int32 carrier_id_table_version = 10;
+
+ /* Data Call session statistics and information. */
+ repeated DataCallSession data_call_session = 11;
+
+ /* Timestamp of last data_call_session pull. */
+ optional int64 data_call_session_pull_timestamp_millis = 12;
+
+ /* Duration spent in each possible service state. */
+ repeated CellularServiceState cellular_service_state = 13;
+
+ /* Timestamp of last cellular_service_state pull. */
+ optional int64 cellular_service_state_pull_timestamp_millis = 14;
+
+ /* Switch count between data RATs. */
+ repeated CellularDataServiceSwitch cellular_data_service_switch = 15;
+
+ /* Timestamp of last cellular_data_service_switch pull. */
+ optional int64 cellular_data_service_switch_pull_timestamp_millis = 16;
+
+ /* List of IMS registration terminations. */
+ repeated ImsRegistrationTermination ims_registration_termination = 17;
+
+ /* Timestamp of last ims_registration_termination pull. */
+ optional int64 ims_registration_termination_pull_timestamp_millis = 18;
+
+ /* Durations of IMS registrations and capabilities. */
+ repeated ImsRegistrationStats ims_registration_stats = 19;
+
+ /* Timestamp of last ims_registration_stats pull. */
+ optional int64 ims_registration_stats_pull_timestamp_millis = 20;
}
// The canonical versions of the following enums live in:
@@ -70,15 +118,142 @@
optional bool rtt_enabled = 22;
optional bool is_emergency = 23;
optional bool is_roaming = 24;
+ optional int32 signal_strength_at_end = 25;
+ optional int32 band_at_end = 26;
+ optional int32 setup_duration_millis = 27;
+ optional int32 main_codec_quality = 28;
+ optional bool video_enabled = 29;
+ optional int32 rat_at_connected = 30;
+ optional bool is_multiparty = 31;
// Internal use only
optional int64 setup_begin_millis = 10001;
}
-// Internal use only
-message RawVoiceCallRatUsage {
+message VoiceCallRatUsage {
optional int32 carrier_id = 1;
optional int32 rat = 2;
- optional int64 total_duration_millis = 3;
+ optional int64 total_duration_millis = 3; // Duration needs to be rounded when pulled
optional int64 call_count = 4;
}
+
+message IncomingSms {
+ optional int32 sms_format = 1;
+ optional int32 sms_tech = 2;
+ optional int32 rat = 3;
+ optional int32 sms_type = 4;
+ optional int32 total_parts = 5;
+ optional int32 received_parts = 6;
+ optional bool blocked = 7;
+ optional int32 error = 8;
+ optional bool is_roaming = 9;
+ optional int32 sim_slot_index = 10;
+ optional bool is_multi_sim = 11;
+ optional bool is_esim = 12;
+ optional int32 carrier_id = 13;
+ optional int64 message_id = 14;
+}
+
+message OutgoingSms {
+ optional int32 sms_format = 1;
+ optional int32 sms_tech = 2;
+ optional int32 rat = 3;
+ optional int32 send_result = 4;
+ optional int32 error_code = 5;
+ optional bool is_roaming = 6;
+ optional bool is_from_default_app = 7;
+ optional int32 sim_slot_index = 8;
+ optional bool is_multi_sim = 9;
+ optional bool is_esim = 10;
+ optional int32 carrier_id = 11;
+ optional int64 message_id = 12;
+ optional int32 retry_id = 13;
+}
+
+message CarrierIdMismatch {
+ optional string mcc_mnc = 1;
+ optional string gid1 = 2;
+ optional string spn = 3;
+ optional string pnn = 4;
+}
+
+message DataCallSession {
+ reserved 4;
+ optional int32 dimension = 1;
+ optional bool is_multi_sim = 2;
+ optional bool is_esim = 3;
+ optional int32 apn_type_bitmask = 5;
+ optional int32 carrier_id = 6;
+ optional bool is_roaming = 7;
+ optional int32 rat_at_end = 8;
+ optional bool oos_at_end = 9;
+ optional int64 rat_switch_count = 10;
+ optional bool is_opportunistic = 11;
+ optional int32 ip_type = 12;
+ optional bool setup_failed = 13;
+ optional int32 failure_cause = 14;
+ optional int32 suggested_retry_millis = 15;
+ optional int32 deactivate_reason = 16;
+ optional int64 duration_minutes = 17;
+ optional bool ongoing = 18;
+}
+
+message CellularServiceState {
+ optional int32 voice_rat = 1;
+ optional int32 data_rat = 2;
+ optional int32 voice_roaming_type = 3;
+ optional int32 data_roaming_type = 4;
+ optional bool is_endc = 5;
+ optional int32 sim_slot_index = 6;
+ optional bool is_multi_sim = 7;
+ optional int32 carrier_id = 8;
+ optional int64 total_time_millis = 9; // Duration needs to be rounded when pulled
+
+ // Internal use only
+ optional int64 last_used_millis = 10001;
+}
+
+message CellularDataServiceSwitch {
+ optional int32 rat_from = 1;
+ optional int32 rat_to = 2;
+ optional int32 sim_slot_index = 3;
+ optional bool is_multi_sim = 4;
+ optional int32 carrier_id = 5;
+ optional int32 switch_count = 6;
+
+ // Internal use only
+ optional int64 last_used_millis = 10001;
+}
+
+message ImsRegistrationTermination {
+ optional int32 carrier_id = 1;
+ optional bool is_multi_sim = 2;
+ optional int32 rat_at_end = 3;
+ optional bool setup_failed = 4;
+ optional int32 reason_code = 5;
+ optional int32 extra_code = 6;
+ optional string extra_message = 7;
+ optional int32 count = 8;
+
+ // Internal use only
+ optional int64 last_used_millis = 10001;
+}
+
+message ImsRegistrationStats {
+ optional int32 carrier_id = 1;
+ optional int32 sim_slot_index = 2;
+ optional int32 rat = 3;
+ // Durations need to be rounded when pulled
+ optional int64 registered_millis = 4;
+ optional int64 voice_capable_millis = 5;
+ optional int64 voice_available_millis = 6;
+ optional int64 sms_capable_millis = 7;
+ optional int64 sms_available_millis = 8;
+ optional int64 video_capable_millis = 9;
+ optional int64 video_available_millis = 10;
+ optional int64 ut_capable_millis = 11;
+ optional int64 ut_available_millis = 12;
+
+ // Internal use only
+ optional int64 last_used_millis = 10001;
+}
diff --git a/src/java/com/android/internal/telephony/CarrierResolver.java b/src/java/com/android/internal/telephony/CarrierResolver.java
index 207d078..56b37a5 100644
--- a/src/java/com/android/internal/telephony/CarrierResolver.java
+++ b/src/java/com/android/internal/telephony/CarrierResolver.java
@@ -16,7 +16,6 @@
package com.android.internal.telephony;
import static android.provider.Telephony.CarrierId;
-import static android.provider.Telephony.Carriers.CONTENT_URI;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -41,6 +40,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.metrics.CarrierIdMatchStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccRecords;
import com.android.internal.telephony.uicc.UiccController;
@@ -78,6 +78,8 @@
private static final String TEST_ACTION = "com.android.internal.telephony"
+ ".ACTION_TEST_OVERRIDE_CARRIER_ID";
+ // cached version of the carrier list, so that we don't need to re-query it every time.
+ private Integer mCarrierListVersion;
// cached matching rules based mccmnc to speed up resolution
private List<CarrierMatchingRule> mCarrierMatchingRulesOnMccMnc = new ArrayList<>();
// cached carrier Id
@@ -275,6 +277,8 @@
handleSimLoaded();
break;
case CARRIER_ID_DB_UPDATE_EVENT:
+ // clean the cached carrier list version, so that a new one will be queried.
+ mCarrierListVersion = null;
loadCarrierMatchingRulesOnMccMnc(true /* update carrier config*/);
break;
case PREFER_APN_UPDATE_EVENT:
@@ -327,6 +331,9 @@
mCarrierMatchingRulesOnMccMnc.add(makeCarrierMatchingRule(cursor));
}
matchSubscriptionCarrier(updateCarrierConfig);
+
+ // Generate metrics related to carrier ID table version.
+ CarrierIdMatchStats.sendCarrierIdTableVersion(getCarrierListVersion());
}
} finally {
if (cursor != null) {
@@ -931,14 +938,26 @@
TelephonyMetrics.getInstance().writeCarrierIdMatchingEvent(
mPhone.getPhoneId(), getCarrierListVersion(), mCarrierId,
unknownMccmncToLog, unknownGid1ToLog, simInfo);
+
+ // Generate statsd metrics only when MCC/MNC is unknown or there is no match for GID1.
+ if (unknownMccmncToLog != null || unknownGid1ToLog != null) {
+ // Pass the PNN value to metrics only if the SPN is empty
+ String pnn = TextUtils.isEmpty(subscriptionRule.spn) ? subscriptionRule.plmn : "";
+ CarrierIdMatchStats.onCarrierIdMismatch(
+ mCarrierId, unknownMccmncToLog, unknownGid1ToLog, subscriptionRule.spn, pnn);
+ }
}
public int getCarrierListVersion() {
- final Cursor cursor = mContext.getContentResolver().query(
- Uri.withAppendedPath(CarrierId.All.CONTENT_URI,
- "get_version"), null, null, null);
- cursor.moveToFirst();
- return cursor.getInt(0);
+ // Use the cached value if it exists, otherwise retrieve it.
+ if (mCarrierListVersion == null) {
+ final Cursor cursor = mContext.getContentResolver().query(
+ Uri.withAppendedPath(CarrierId.All.CONTENT_URI,
+ "get_version"), null, null, null);
+ cursor.moveToFirst();
+ mCarrierListVersion = cursor.getInt(0);
+ }
+ return mCarrierListVersion;
}
public int getCarrierId() {
diff --git a/src/java/com/android/internal/telephony/DeviceStateMonitor.java b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
index 9e95939..94de555 100644
--- a/src/java/com/android/internal/telephony/DeviceStateMonitor.java
+++ b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
@@ -628,26 +628,31 @@
}
private void setSignalStrengthReportingCriteria() {
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSSI,
+ mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
AccessNetworkThresholds.GERAN, AccessNetworkType.GERAN, true);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSCP,
+ mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP,
AccessNetworkThresholds.UTRAN, AccessNetworkType.UTRAN, true);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSRP,
+ mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
AccessNetworkThresholds.EUTRAN_RSRP, AccessNetworkType.EUTRAN, true);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSSI,
+ mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
AccessNetworkThresholds.CDMA2000, AccessNetworkType.CDMA2000, true);
if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSRQ,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
AccessNetworkThresholds.EUTRAN_RSRQ, AccessNetworkType.EUTRAN, false);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSSNR,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR,
AccessNetworkThresholds.EUTRAN_RSSNR, AccessNetworkType.EUTRAN, true);
// Defaultly we only need SSRSRP for NGRAN signal criteria reporting
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSRSRP,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
AccessNetworkThresholds.NGRAN_RSRSRP, AccessNetworkType.NGRAN, true);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSRSRQ,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
AccessNetworkThresholds.NGRAN_RSRSRQ, AccessNetworkType.NGRAN, false);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSSINR,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR,
AccessNetworkThresholds.NGRAN_SSSINR, AccessNetworkType.NGRAN, false);
}
}
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 0f59eb2..812a0c6 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -1876,37 +1876,45 @@
@Override
public int getCarrierId() {
- return mCarrierResolver.getCarrierId();
+ return mCarrierResolver != null
+ ? mCarrierResolver.getCarrierId() : super.getCarrierId();
}
@Override
public String getCarrierName() {
- return mCarrierResolver.getCarrierName();
+ return mCarrierResolver != null
+ ? mCarrierResolver.getCarrierName() : super.getCarrierName();
}
@Override
public int getMNOCarrierId() {
- return mCarrierResolver.getMnoCarrierId();
+ return mCarrierResolver != null
+ ? mCarrierResolver.getMnoCarrierId() : super.getMNOCarrierId();
}
@Override
public int getSpecificCarrierId() {
- return mCarrierResolver.getSpecificCarrierId();
+ return mCarrierResolver != null
+ ? mCarrierResolver.getSpecificCarrierId() : super.getSpecificCarrierId();
}
@Override
public String getSpecificCarrierName() {
- return mCarrierResolver.getSpecificCarrierName();
+ return mCarrierResolver != null
+ ? mCarrierResolver.getSpecificCarrierName() : super.getSpecificCarrierName();
}
@Override
public void resolveSubscriptionCarrierId(String simState) {
- mCarrierResolver.resolveSubscriptionCarrierId(simState);
+ if (mCarrierResolver != null) {
+ mCarrierResolver.resolveSubscriptionCarrierId(simState);
+ }
}
@Override
public int getCarrierIdListVersion() {
- return mCarrierResolver.getCarrierListVersion();
+ return mCarrierResolver != null
+ ? mCarrierResolver.getCarrierListVersion() : super.getCarrierIdListVersion();
}
@Override
@@ -4046,8 +4054,15 @@
@Override
public void setSignalStrengthReportingCriteria(
int signalStrengthMeasure, int[] thresholds, int ran, boolean isEnabled) {
- mCi.setSignalStrengthReportingCriteria(new SignalThresholdInfo(signalStrengthMeasure,
- REPORTING_HYSTERESIS_MILLIS, REPORTING_HYSTERESIS_DB, thresholds, isEnabled),
+ mCi.setSignalStrengthReportingCriteria(
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(ran)
+ .setSignalMeasurementType(signalStrengthMeasure)
+ .setHysteresisMs(REPORTING_HYSTERESIS_MILLIS)
+ .setHysteresisDb(REPORTING_HYSTERESIS_DB)
+ .setThresholds(thresholds)
+ .setIsEnabled(isEnabled)
+ .build(),
ran, null);
}
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 4815dea..a1a9578 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -628,7 +628,7 @@
"\n format=" + format +
"\n receivedIntent=" + receivedIntent);
}
- mDispatchersController.injectSmsPdu(pdu, format,
+ mDispatchersController.injectSmsPdu(pdu, format, false /* isOverIms */,
result -> {
if (receivedIntent != null) {
try {
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
index 722390fb..eb96af2 100644
--- a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -23,6 +23,7 @@
import android.provider.Telephony.Sms.Intents;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
+import android.telephony.SmsManager;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.RegistrationManager;
import android.telephony.ims.aidl.IImsSmsListener;
@@ -139,7 +140,7 @@
private final IImsSmsListener mImsSmsListener = new IImsSmsListener.Stub() {
@Override
public void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
- int reason, int networkReasonCode) {
+ @SmsManager.Result int reason, int networkReasonCode) {
final long identity = Binder.clearCallingIdentity();
try {
logd("onSendSmsResult token=" + token + " messageRef=" + messageRef
@@ -178,6 +179,13 @@
break;
default:
}
+ mPhone.getSmsStats().onOutgoingSms(
+ true /* isOverIms */,
+ SmsConstants.FORMAT_3GPP2.equals(getFormat()),
+ status == ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK,
+ reason,
+ tracker.mMessageId,
+ tracker.isFromDefaultSmsApplication(mContext));
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -245,7 +253,7 @@
} catch (ImsException e) {
loge("Failed to acknowledgeSms(). Error: " + e.getMessage());
}
- }, true);
+ }, true /* ignoreClass */, true /* isOverIms */);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -436,6 +444,13 @@
fallbackToPstn(tracker);
mMetrics.writeImsServiceSendSms(mPhone.getPhoneId(), format,
ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK, tracker.mMessageId);
+ mPhone.getSmsStats().onOutgoingSms(
+ true /* isOverIms */,
+ SmsConstants.FORMAT_3GPP2.equals(format),
+ true /* fallbackToCs */,
+ SmsManager.RESULT_SYSTEM_ERROR,
+ tracker.mMessageId,
+ tracker.isFromDefaultSmsApplication(mContext));
}
}
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index f42ba06..c3de17c 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -24,6 +24,7 @@
import static android.service.carrier.CarrierMessagingService.RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE;
import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.AppOpsManager;
@@ -80,6 +81,8 @@
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -183,6 +186,24 @@
/** Wakelock release delay when returning to idle state. */
private static final int WAKELOCK_TIMEOUT = 3000;
+ /** Received SMS was not injected. */
+ public static final int SOURCE_NOT_INJECTED = 0;
+
+ /** Received SMS was received over IMS and injected. */
+ public static final int SOURCE_INJECTED_FROM_IMS = 1;
+
+ /** Received SMS was injected from source different than IMS. */
+ public static final int SOURCE_INJECTED_FROM_UNKNOWN = 2;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"SOURCE_"},
+ value = {
+ SOURCE_NOT_INJECTED,
+ SOURCE_INJECTED_FROM_IMS,
+ SOURCE_INJECTED_FROM_UNKNOWN
+ })
+ public @interface SmsSource {}
+
// The notitfication tag used when showing a notification. The combination of notification tag
// and notification id should be unique within the phone app.
private static final String NOTIFICATION_TAG = "InboundSmsHandler";
@@ -255,10 +276,6 @@
/** Timeout for releasing wakelock */
private int mWakeLockTimeout;
- /** Indicates if last SMS was injected. This is used to recognize SMS received over IMS from
- others in order to update metrics. */
- private boolean mLastSmsWasInjected = false;
-
private List<SmsFilter> mSmsFilters;
/**
@@ -516,7 +533,7 @@
case EVENT_INJECT_SMS:
// handle new injected SMS
- handleInjectSms((AsyncResult) msg.obj);
+ handleInjectSms((AsyncResult) msg.obj, msg.arg1 == 1 /* isOverIms */);
sendMessage(EVENT_RETURN_TO_IDLE);
return HANDLED;
@@ -641,8 +658,7 @@
int result;
try {
SmsMessage sms = (SmsMessage) ar.result;
- mLastSmsWasInjected = false;
- result = dispatchMessage(sms.mWrappedSmsMessage);
+ result = dispatchMessage(sms.mWrappedSmsMessage, SOURCE_NOT_INJECTED);
} catch (RuntimeException ex) {
loge("Exception dispatching message", ex);
result = RESULT_SMS_DISPATCH_FAILURE;
@@ -661,7 +677,7 @@
* @param ar is the AsyncResult that has the SMS PDU to be injected.
*/
@UnsupportedAppUsage
- private void handleInjectSms(AsyncResult ar) {
+ private void handleInjectSms(AsyncResult ar, boolean isOverIms) {
int result;
SmsDispatchersController.SmsInjectionCallback callback = null;
try {
@@ -671,8 +687,9 @@
loge("Null injected sms");
result = RESULT_SMS_NULL_PDU;
} else {
- mLastSmsWasInjected = true;
- result = dispatchMessage(sms.mWrappedSmsMessage);
+ @SmsSource int smsSource =
+ isOverIms ? SOURCE_INJECTED_FROM_IMS : SOURCE_INJECTED_FROM_UNKNOWN;
+ result = dispatchMessage(sms.mWrappedSmsMessage, smsSource);
}
} catch (RuntimeException ex) {
loge("Exception dispatching message", ex);
@@ -689,10 +706,11 @@
* 3GPP2-specific message types.
*
* @param smsb the SmsMessageBase object from the RIL
+ * @param smsSource the source of the SMS message
* @return a result code from {@link android.provider.Telephony.Sms.Intents},
* or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
*/
- private int dispatchMessage(SmsMessageBase smsb) {
+ private int dispatchMessage(SmsMessageBase smsb, @SmsSource int smsSource) {
// If sms is null, there was a parsing error.
if (smsb == null) {
loge("dispatchSmsMessage: message is null");
@@ -719,12 +737,13 @@
return Intents.RESULT_SMS_RECEIVED_WHILE_ENCRYPTED;
}
- int result = dispatchMessageRadioSpecific(smsb);
+ int result = dispatchMessageRadioSpecific(smsb, smsSource);
// In case of error, add to metrics. This is not required in case of success, as the
// data will be tracked when the message is processed (processMessagePart).
- if (result != Intents.RESULT_SMS_HANDLED) {
- mMetrics.writeIncomingSmsError(mPhone.getPhoneId(), mLastSmsWasInjected, result);
+ if (result != Intents.RESULT_SMS_HANDLED && result != Activity.RESULT_OK) {
+ mMetrics.writeIncomingSmsError(mPhone.getPhoneId(), smsSource, result);
+ mPhone.getSmsStats().onIncomingSmsError(is3gpp2(), smsSource, result);
}
return result;
}
@@ -735,10 +754,12 @@
* {@link #dispatchNormalMessage} from this class.
*
* @param smsb the SmsMessageBase object from the RIL
+ * @param smsSource the source of the SMS message
* @return a result code from {@link android.provider.Telephony.Sms.Intents},
* or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
*/
- protected abstract int dispatchMessageRadioSpecific(SmsMessageBase smsb);
+ protected abstract int dispatchMessageRadioSpecific(SmsMessageBase smsb,
+ @SmsSource int smsSource);
/**
* Send an acknowledge message to the SMSC.
@@ -782,10 +803,11 @@
* {@link #EVENT_BROADCAST_SMS}. Returns {@link Intents#RESULT_SMS_HANDLED} or an error value.
*
* @param sms the message to dispatch
+ * @param smsSource the source of the SMS message
* @return {@link Intents#RESULT_SMS_HANDLED} if the message was accepted, or an error status
*/
@UnsupportedAppUsage
- protected int dispatchNormalMessage(SmsMessageBase sms) {
+ protected int dispatchNormalMessage(SmsMessageBase sms, @SmsSource int smsSource) {
SmsHeader smsHeader = sms.getUserDataHeader();
InboundSmsTracker tracker;
@@ -800,10 +822,10 @@
tracker = TelephonyComponentFactory.getInstance()
.inject(InboundSmsTracker.class.getName())
.makeInboundSmsTracker(mContext, sms.getPdu(),
- sms.getTimestampMillis(), destPort, is3gpp2(), false,
- sms.getOriginatingAddress(), sms.getDisplayOriginatingAddress(),
- sms.getMessageBody(), sms.getMessageClass() == MessageClass.CLASS_0,
- mPhone.getSubId());
+ sms.getTimestampMillis(), destPort, is3gpp2(), false,
+ sms.getOriginatingAddress(), sms.getDisplayOriginatingAddress(),
+ sms.getMessageBody(), sms.getMessageClass() == MessageClass.CLASS_0,
+ mPhone.getSubId(), smsSource);
} else {
// Create a tracker for this message segment.
SmsHeader.ConcatRef concatRef = smsHeader.concatRef;
@@ -812,10 +834,11 @@
tracker = TelephonyComponentFactory.getInstance()
.inject(InboundSmsTracker.class.getName())
.makeInboundSmsTracker(mContext, sms.getPdu(),
- sms.getTimestampMillis(), destPort, is3gpp2(), sms.getOriginatingAddress(),
- sms.getDisplayOriginatingAddress(), concatRef.refNumber, concatRef.seqNumber,
- concatRef.msgCount, false, sms.getMessageBody(),
- sms.getMessageClass() == MessageClass.CLASS_0, mPhone.getSubId());
+ sms.getTimestampMillis(), destPort, is3gpp2(),
+ sms.getOriginatingAddress(), sms.getDisplayOriginatingAddress(),
+ concatRef.refNumber, concatRef.seqNumber, concatRef.msgCount, false,
+ sms.getMessageBody(), sms.getMessageClass() == MessageClass.CLASS_0,
+ mPhone.getSubId(), smsSource);
}
if (VDBG) log("created tracker: " + tracker);
@@ -975,14 +998,7 @@
}
final boolean isWapPush = (destPort == SmsHeader.PORT_WAP_PUSH);
-
- // At this point, all parts of the SMS are received. Update metrics for incoming SMS.
- // WAP-PUSH messages are handled below to also keep track of the result of the processing.
String format = tracker.getFormat();
- if (!isWapPush) {
- mMetrics.writeIncomingSmsSession(mPhone.getPhoneId(), mLastSmsWasInjected,
- format, timestamps, block, tracker.getMessageId());
- }
// Do not process null pdu(s). Check for that and return false in that case.
List<byte[]> pduList = Arrays.asList(pdus);
@@ -990,6 +1006,8 @@
String errorMsg = "processMessagePart: returning false due to "
+ (pduList.size() == 0 ? "pduList.size() == 0" : "pduList.contains(null)");
logeWithLocalLog(errorMsg, tracker.getMessageId());
+ mPhone.getSmsStats().onIncomingSmsError(
+ is3gpp2(), tracker.getSource(), RESULT_SMS_NULL_PDU);
return false;
}
@@ -1004,9 +1022,11 @@
} else {
loge("processMessagePart: SmsMessage.createFromPdu returned null",
tracker.getMessageId());
- mMetrics.writeIncomingWapPush(mPhone.getPhoneId(), mLastSmsWasInjected,
+ mMetrics.writeIncomingWapPush(mPhone.getPhoneId(), tracker.getSource(),
SmsConstants.FORMAT_3GPP, timestamps, false,
tracker.getMessageId());
+ mPhone.getSmsStats().onIncomingSmsWapPush(tracker.getSource(),
+ messageCount, RESULT_SMS_NULL_MESSAGE, tracker.getMessageId());
return false;
}
}
@@ -1035,13 +1055,12 @@
}
// Add result of WAP-PUSH into metrics. RESULT_SMS_HANDLED indicates that the WAP-PUSH
// needs to be ignored, so treating it as a success case.
- if (result == Activity.RESULT_OK || result == Intents.RESULT_SMS_HANDLED) {
- mMetrics.writeIncomingWapPush(mPhone.getPhoneId(), mLastSmsWasInjected,
- format, timestamps, true, tracker.getMessageId());
- } else {
- mMetrics.writeIncomingWapPush(mPhone.getPhoneId(), mLastSmsWasInjected,
- format, timestamps, false, tracker.getMessageId());
- }
+ boolean wapPushResult =
+ result == Activity.RESULT_OK || result == Intents.RESULT_SMS_HANDLED;
+ mMetrics.writeIncomingWapPush(mPhone.getPhoneId(), tracker.getSource(),
+ format, timestamps, wapPushResult, tracker.getMessageId());
+ mPhone.getSmsStats().onIncomingSmsWapPush(tracker.getSource(), messageCount,
+ result, tracker.getMessageId());
// result is Activity.RESULT_OK if an ordered broadcast was sent
if (result == Activity.RESULT_OK) {
return true;
@@ -1054,6 +1073,15 @@
}
}
+ // All parts of SMS are received. Update metrics for incoming SMS.
+ // The metrics are generated before SMS filters are invoked.
+ // For messages composed by multiple parts, the metrics are generated considering the
+ // characteristics of the last one.
+ mMetrics.writeIncomingSmsSession(mPhone.getPhoneId(), tracker.getSource(),
+ format, timestamps, block, tracker.getMessageId());
+ mPhone.getSmsStats().onIncomingSmsSuccess(is3gpp2(), tracker.getSource(),
+ messageCount, block, tracker.getMessageId());
+
// Always invoke SMS filters, even if the number ends up being blocked, to prevent
// surprising bugs due to blocking numbers that happen to be used for visual voicemail SMS
// or other carrier system messages.
diff --git a/src/java/com/android/internal/telephony/InboundSmsTracker.java b/src/java/com/android/internal/telephony/InboundSmsTracker.java
index d3306a6..9ddee8d 100644
--- a/src/java/com/android/internal/telephony/InboundSmsTracker.java
+++ b/src/java/com/android/internal/telephony/InboundSmsTracker.java
@@ -56,6 +56,7 @@
private final boolean mIsClass0;
private final int mSubId;
private final long mMessageId;
+ private final @InboundSmsHandler.SmsSource int mSmsSource;
// Fields for concatenating multi-part SMS messages
private final String mAddress;
@@ -117,10 +118,12 @@
* @param address originating address
* @param displayAddress email address if this message was from an email gateway, otherwise same
* as originating address
+ * @param smsSource the source of the SMS message
*/
public InboundSmsTracker(Context context, byte[] pdu, long timestamp, int destPort,
boolean is3gpp2, boolean is3gpp2WapPdu, String address, String displayAddress,
- String messageBody, boolean isClass0, int subId) {
+ String messageBody, boolean isClass0, int subId,
+ @InboundSmsHandler.SmsSource int smsSource) {
mPdu = pdu;
mTimestamp = timestamp;
mDestPort = destPort;
@@ -136,6 +139,7 @@
mMessageCount = 1;
mSubId = subId;
mMessageId = createMessageId(context, timestamp, subId);
+ mSmsSource = smsSource;
}
/**
@@ -156,11 +160,12 @@
* @param sequenceNumber the sequence number of this segment (0-based)
* @param messageCount the total number of segments
* @param is3gpp2WapPdu true for 3GPP2 format WAP PDU; false otherwise
+ * @param smsSource the source of the SMS message
*/
public InboundSmsTracker(Context context, byte[] pdu, long timestamp, int destPort,
boolean is3gpp2, String address, String displayAddress, int referenceNumber,
int sequenceNumber, int messageCount, boolean is3gpp2WapPdu, String messageBody,
- boolean isClass0, int subId) {
+ boolean isClass0, int subId, @InboundSmsHandler.SmsSource int smsSource) {
mPdu = pdu;
mTimestamp = timestamp;
mDestPort = destPort;
@@ -177,6 +182,7 @@
mMessageCount = messageCount;
mSubId = subId;
mMessageId = createMessageId(context, timestamp, subId);
+ mSmsSource = smsSource;
}
/**
@@ -241,6 +247,8 @@
}
mMessageBody = cursor.getString(InboundSmsHandler.MESSAGE_BODY_COLUMN);
mMessageId = createMessageId(context, mTimestamp, mSubId);
+ // TODO(b/167713264): Use the correct SMS source
+ mSmsSource = InboundSmsHandler.SOURCE_NOT_INJECTED;
}
public ContentValues getContentValues() {
@@ -497,4 +505,8 @@
public long getMessageId() {
return mMessageId;
}
+
+ public @InboundSmsHandler.SmsSource int getSource() {
+ return mSmsSource;
+ }
}
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 6180f7a..b7fa377 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -81,6 +81,7 @@
import com.android.internal.telephony.dataconnection.TransportManager;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.imsphone.ImsPhoneCall;
+import com.android.internal.telephony.metrics.SmsStats;
import com.android.internal.telephony.metrics.VoiceCallSessionStats;
import com.android.internal.telephony.test.SimulatedRadioControl;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
@@ -444,6 +445,7 @@
private final CarrierPrivilegesTracker mCarrierPrivilegesTracker;
protected VoiceCallSessionStats mVoiceCallSessionStats;
+ protected SmsStats mSmsStats;
public IccRecords getIccRecords() {
return mIccRecords.get();
@@ -585,6 +587,9 @@
mCallRingDelay = TelephonyProperties.call_ring_delay().orElse(3000);
Rlog.d(LOG_TAG, "mCallRingDelay=" + mCallRingDelay);
+ // Initialize SMS stats
+ mSmsStats = new SmsStats(this);
+
if (getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
return;
}
@@ -4573,6 +4578,17 @@
mVoiceCallSessionStats = voiceCallSessionStats;
}
+ /** Returns the {@link SmsStats} for this phone ID. */
+ public SmsStats getSmsStats() {
+ return mSmsStats;
+ }
+
+ /** Sets the {@link SmsStats} mock for this phone ID during unit testing. */
+ @VisibleForTesting
+ public void setSmsStats(SmsStats smsStats) {
+ mSmsStats = smsStats;
+ }
+
/** @hide */
public CarrierPrivilegesTracker getCarrierPrivilegesTracker() {
return mCarrierPrivilegesTracker;
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index c2481f6..65bd62f 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -117,6 +117,7 @@
import com.android.internal.telephony.cdma.CdmaInformationRecords;
import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import com.android.internal.telephony.metrics.ModemRestartStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
@@ -5319,7 +5320,7 @@
SignalThresholdInfo signalThresholdInfo) {
android.hardware.radio.V1_5.SignalThresholdInfo signalThresholdInfoHal =
new android.hardware.radio.V1_5.SignalThresholdInfo();
- signalThresholdInfoHal.signalMeasurement = signalThresholdInfo.getSignalMeasurement();
+ signalThresholdInfoHal.signalMeasurement = signalThresholdInfo.getSignalMeasurementType();
signalThresholdInfoHal.hysteresisMs = signalThresholdInfo.getHysteresisMs();
signalThresholdInfoHal.hysteresisDb = signalThresholdInfo.getHysteresisDb();
signalThresholdInfoHal.thresholds = primitiveArrayToArrayList(
@@ -6442,6 +6443,11 @@
void writeMetricsModemRestartEvent(String reason) {
mMetrics.writeModemRestartEvent(mPhoneId, reason);
+ // Write metrics to statsd. Generate metric only when modem reset is detected by the
+ // first instance of RIL to avoid duplicated events.
+ if (mPhoneId == 0) {
+ ModemRestartStats.onModemRestart(reason);
+ }
}
/**
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index d375909..71ca823 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -751,9 +751,10 @@
protected void handleSendComplete(AsyncResult ar) {
SmsTracker tracker = (SmsTracker) ar.userObj;
PendingIntent sentIntent = tracker.mSentIntent;
+ SmsResponse smsResponse = (SmsResponse) ar.result;
- if (ar.result != null) {
- tracker.mMessageRef = ((SmsResponse)ar.result).mMessageRef;
+ if (smsResponse != null) {
+ tracker.mMessageRef = smsResponse.mMessageRef;
} else {
Rlog.d(TAG, "SmsResponse was null");
}
@@ -770,15 +771,22 @@
}
tracker.onSent(mContext);
mPhone.notifySmsSent(tracker.mDestAddress);
+
+ mPhone.getSmsStats().onOutgoingSms(
+ tracker.mImsRetry > 0 /* isOverIms */,
+ SmsConstants.FORMAT_3GPP2.equals(getFormat()),
+ false /* fallbackToCs */,
+ SmsManager.RESULT_ERROR_NONE,
+ tracker.mMessageId,
+ tracker.isFromDefaultSmsApplication(mContext));
} else {
if (DBG) {
- Rlog.d(TAG, "SMS send failed"
- + " id: " + tracker.mMessageId);
+ Rlog.d(TAG, "SMS send failed id: " + tracker.mMessageId);
}
int ss = mPhone.getServiceState().getState();
- if ( tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) {
+ if (tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) {
// This is retry after failure over IMS but voice is not available.
// Set retry to max allowed, so no retry is sent and
// cause RESULT_ERROR_GENERIC_FAILURE to be returned to app.
@@ -796,6 +804,13 @@
// if sms over IMS is not supported on data and voice is not available...
if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
tracker.onFailed(mContext, getNotInServiceError(ss), NO_ERROR_CODE);
+ mPhone.getSmsStats().onOutgoingSms(
+ tracker.mImsRetry > 0 /* isOverIms */,
+ SmsConstants.FORMAT_3GPP2.equals(getFormat()),
+ false /* fallbackToCs */,
+ getNotInServiceError(ss),
+ tracker.mMessageId,
+ tracker.isFromDefaultSmsApplication(mContext));
} else if ((((CommandException)(ar.exception)).getCommandError()
== CommandException.Error.SMS_FAIL_RETRY) &&
tracker.mRetryCount < MAX_SEND_RETRIES) {
@@ -808,20 +823,35 @@
// message, depending on the failure). Also, in some
// implementations this retry is handled by the baseband.
tracker.mRetryCount++;
+ int errorCode = (smsResponse != null) ? smsResponse.mErrorCode : NO_ERROR_CODE;
Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);
sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);
+ mPhone.getSmsStats().onOutgoingSms(
+ tracker.mImsRetry > 0 /* isOverIms */,
+ SmsConstants.FORMAT_3GPP2.equals(getFormat()),
+ false /* fallbackToCs */,
+ SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY,
+ errorCode,
+ tracker.mMessageId,
+ tracker.isFromDefaultSmsApplication(mContext));
} else {
- int errorCode = NO_ERROR_CODE;
- if (ar.result != null) {
- errorCode = ((SmsResponse)ar.result).mErrorCode;
- }
+ int errorCode = (smsResponse != null) ? smsResponse.mErrorCode : NO_ERROR_CODE;
int error = rilErrorToSmsManagerResult(((CommandException) (ar.exception))
.getCommandError());
tracker.onFailed(mContext, error, errorCode);
+ mPhone.getSmsStats().onOutgoingSms(
+ tracker.mImsRetry > 0 /* isOverIms */,
+ SmsConstants.FORMAT_3GPP2.equals(getFormat()),
+ false /* fallbackToCs */,
+ error,
+ errorCode,
+ tracker.mMessageId,
+ tracker.isFromDefaultSmsApplication(mContext));
}
}
}
+ @SmsManager.Result
private static int rilErrorToSmsManagerResult(CommandException.Error rilError) {
switch (rilError) {
case RADIO_NOT_AVAILABLE:
@@ -881,6 +911,7 @@
* @param ss service state
* @return The result error based on input service state for not in service error
*/
+ @SmsManager.Result
protected static int getNotInServiceError(int ss) {
if (ss == ServiceState.STATE_POWER_OFF) {
return RESULT_ERROR_RADIO_OFF;
@@ -1294,7 +1325,7 @@
*/
@VisibleForTesting
public void sendRawPdu(SmsTracker[] trackers) {
- int error = RESULT_ERROR_NONE;
+ @SmsManager.Result int error = RESULT_ERROR_NONE;
PackageInfo appInfo = null;
if (mSmsSendDisabled) {
Rlog.e(TAG, "Device does not support sending sms.");
@@ -1610,10 +1641,22 @@
}
}
- private void handleSmsTrackersFailure(SmsTracker[] trackers, int error, int errorCode) {
+ private void handleSmsTrackersFailure(SmsTracker[] trackers, @SmsManager.Result int error,
+ int errorCode) {
for (SmsTracker tracker : trackers) {
tracker.onFailed(mContext, error, errorCode);
}
+ if (trackers.length > 0) {
+ // This error occurs before the SMS is sent. Make an assumption if it would have
+ // been sent over IMS or not.
+ mPhone.getSmsStats().onOutgoingSms(
+ isIms(),
+ SmsConstants.FORMAT_3GPP2.equals(getFormat()),
+ false /* fallbackToCs */,
+ error,
+ trackers[0].mMessageId,
+ trackers[0].isFromDefaultSmsApplication(mContext));
+ }
}
/**
@@ -1678,6 +1721,8 @@
public final long mMessageId;
+ private Boolean mIsFromDefaultSmsApplication;
+
// SMS anomaly uuid
private final UUID mAnomalyUUID = UUID.fromString("43043600-ea7a-44d2-9ae6-a58567ac7886");
@@ -1725,6 +1770,16 @@
return mAppInfo != null ? mAppInfo.packageName : null;
}
+ /** Return if the SMS was originated from the default SMS application. */
+ public boolean isFromDefaultSmsApplication(Context context) {
+ if (mIsFromDefaultSmsApplication == null) {
+ // Perform a lazy initialization, due to the cost of the operation.
+ mIsFromDefaultSmsApplication =
+ SmsApplication.isDefaultSmsApplication(context, getAppPackageName());
+ }
+ return mIsFromDefaultSmsApplication;
+ }
+
/**
* Update the status of this message if we persisted it
*/
@@ -1775,8 +1830,7 @@
* @return The telephony provider URI if stored
*/
private Uri persistSentMessageIfRequired(Context context, int messageType, int errorCode) {
- if (!mIsText || !mPersistMessage ||
- !SmsApplication.shouldWriteMessageForPackage(mAppInfo.packageName, context)) {
+ if (!mIsText || !mPersistMessage || isFromDefaultSmsApplication(context)) {
return null;
}
Rlog.d(TAG, "Persist SMS into "
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index a18c93a..859a8dc 100755
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -99,6 +99,7 @@
import com.android.internal.telephony.dataconnection.DataConnection;
import com.android.internal.telephony.dataconnection.DcTracker;
import com.android.internal.telephony.dataconnection.TransportManager;
+import com.android.internal.telephony.metrics.ServiceStateStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
import com.android.internal.telephony.uicc.IccCardStatus.CardState;
@@ -481,6 +482,8 @@
private static final int MS_PER_HOUR = 60 * 60 * 1000;
private final NitzStateMachine mNitzState;
+ private ServiceStateStats mServiceStateStats;
+
/**
* Holds the last NITZ signal received. Used only for trying to determine an MCC from a CDMA
* SID.
@@ -647,6 +650,8 @@
mPhone = phone;
mCi = ci;
+ mServiceStateStats = new ServiceStateStats(mPhone);
+
mCdnr = new CarrierDisplayNameResolver(mPhone);
mEriManager = TelephonyComponentFactory.getInstance().inject(EriManager.class.getName())
@@ -1717,6 +1722,7 @@
TelephonyMetrics.getInstance().writeServiceStateChanged(
mPhone.getPhoneId(), mSS);
mPhone.getVoiceCallSessionStats().onServiceStateChanged(mSS);
+ mServiceStateStats.onServiceStateChanged(mSS);
}
}
break;
@@ -2301,6 +2307,7 @@
TelephonyMetrics.getInstance().writeServiceStateChanged(
mPhone.getPhoneId(), mSS);
mPhone.getVoiceCallSessionStats().onServiceStateChanged(mSS);
+ mServiceStateStats.onServiceStateChanged(mSS);
}
if (mPhone.isPhoneTypeGsm()) {
@@ -3590,6 +3597,7 @@
if (hasChanged || hasNrStateChanged) {
TelephonyMetrics.getInstance().writeServiceStateChanged(mPhone.getPhoneId(), mSS);
mPhone.getVoiceCallSessionStats().onServiceStateChanged(mSS);
+ mServiceStateStats.onServiceStateChanged(mSS);
}
boolean shouldLogAttachedChange = false;
@@ -4924,38 +4932,43 @@
private void updateReportingCriteria(PersistableBundle config) {
int lteMeasurementEnabled = config.getInt(CarrierConfigManager
.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT, CellSignalStrengthLte.USE_RSRP);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSRP,
+ mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
config.getIntArray(CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY),
AccessNetworkType.EUTRAN,
(lteMeasurementEnabled & CellSignalStrengthLte.USE_RSRP) != 0);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSCP,
+ mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP,
config.getIntArray(CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY),
AccessNetworkType.UTRAN, true);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSSI,
+ mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
config.getIntArray(CarrierConfigManager.KEY_GSM_RSSI_THRESHOLDS_INT_ARRAY),
AccessNetworkType.GERAN, true);
if (mPhone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_5)) {
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSRQ,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
config.getIntArray(CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY),
AccessNetworkType.EUTRAN,
(lteMeasurementEnabled & CellSignalStrengthLte.USE_RSRQ) != 0);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_RSSNR,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR,
config.getIntArray(CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY),
AccessNetworkType.EUTRAN,
(lteMeasurementEnabled & CellSignalStrengthLte.USE_RSSNR) != 0);
int measurementEnabled = config.getInt(CarrierConfigManager
.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT, CellSignalStrengthNr.USE_SSRSRP);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSRSRP,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
config.getIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY),
AccessNetworkType.NGRAN,
(measurementEnabled & CellSignalStrengthNr.USE_SSRSRP) != 0);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSRSRQ,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
config.getIntArray(CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY),
AccessNetworkType.NGRAN,
(measurementEnabled & CellSignalStrengthNr.USE_SSRSRQ) != 0);
- mPhone.setSignalStrengthReportingCriteria(SignalThresholdInfo.SIGNAL_SSSINR,
+ mPhone.setSignalStrengthReportingCriteria(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR,
config.getIntArray(CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY),
AccessNetworkType.NGRAN,
(measurementEnabled & CellSignalStrengthNr.USE_SSSINR) != 0);
@@ -5900,6 +5913,17 @@
tm.setDataNetworkTypeForPhone(mPhone.getPhoneId(), type);
}
+ /** Returns the {@link ServiceStateStats} for the phone tracked. */
+ public ServiceStateStats getServiceStateStats() {
+ return mServiceStateStats;
+ }
+
+ /** Replaces the {@link ServiceStateStats} for testing purposes. */
+ @VisibleForTesting
+ public void setServiceStateStats(ServiceStateStats serviceStateStats) {
+ mServiceStateStats = serviceStateStats;
+ }
+
/**
* Used to insert a ServiceState into the ServiceStateProvider as a ContentValues instance.
*
diff --git a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
index 5008665..40a6f03 100644
--- a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
+++ b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
@@ -16,6 +16,7 @@
package com.android.internal.telephony;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -225,8 +226,10 @@
}
}
}
- // Retrieve the phone id, required for metrics
- int phoneId = getPhoneId(gsmInboundSmsHandler, cdmaInboundSmsHandler);
+ // Retrieve the phone and phone id, required for metrics
+ Phone phone = getPhone(gsmInboundSmsHandler, cdmaInboundSmsHandler);
+ int phoneId = phone != null ? phone.getPhoneId()
+ : SubscriptionManager.INVALID_PHONE_INDEX;
// Delete old incomplete message segments
for (SmsReferenceKey message : oldMultiPartMessages) {
@@ -244,6 +247,10 @@
TelephonyMetrics metrics = TelephonyMetrics.getInstance();
metrics.writeDroppedIncomingMultipartSms(phoneId, message.mFormat, rows,
message.mMessageCount);
+ if (phone != null) {
+ phone.getSmsStats().onDroppedIncomingMultipartSms(message.mIs3gpp2, rows,
+ message.mMessageCount);
+ }
}
}
} catch (SQLException e) {
@@ -258,17 +265,17 @@
}
/**
- * Retrieve the phone id for the GSM or CDMA Inbound SMS handler
+ * Retrieve the phone for the GSM or CDMA Inbound SMS handler
*/
- private static int getPhoneId(GsmInboundSmsHandler gsmInboundSmsHandler,
+ @Nullable
+ private static Phone getPhone(GsmInboundSmsHandler gsmInboundSmsHandler,
CdmaInboundSmsHandler cdmaInboundSmsHandler) {
- int phoneId = SubscriptionManager.INVALID_PHONE_INDEX;
if (gsmInboundSmsHandler != null) {
- phoneId = gsmInboundSmsHandler.getPhone().getPhoneId();
+ return gsmInboundSmsHandler.getPhone();
} else if (cdmaInboundSmsHandler != null) {
- phoneId = cdmaInboundSmsHandler.getPhone().getPhoneId();
+ return cdmaInboundSmsHandler.getPhone();
}
- return phoneId;
+ return null;
}
/**
@@ -312,6 +319,7 @@
final int mReferenceNumber;
final int mMessageCount;
final String mQuery;
+ final boolean mIs3gpp2;
final String mFormat;
SmsReferenceKey(InboundSmsTracker tracker) {
@@ -319,6 +327,7 @@
mReferenceNumber = tracker.getReferenceNumber();
mMessageCount = tracker.getMessageCount();
mQuery = tracker.getQueryForSegments();
+ mIs3gpp2 = tracker.is3gpp2();
mFormat = tracker.getFormat();
}
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index 471b16f..fc3c679 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -387,12 +387,13 @@
* the same time an SMS received from radio is responded back.
*/
@VisibleForTesting
- public void injectSmsPdu(byte[] pdu, String format, SmsInjectionCallback callback) {
+ public void injectSmsPdu(byte[] pdu, String format, boolean isOverIms,
+ SmsInjectionCallback callback) {
// TODO We need to decide whether we should allow injecting GSM(3gpp)
// SMS pdus when the phone is camping on CDMA(3gpp2) network and vice versa.
android.telephony.SmsMessage msg =
android.telephony.SmsMessage.createFromPdu(pdu, format);
- injectSmsPdu(msg, format, callback, false /* ignoreClass */);
+ injectSmsPdu(msg, format, callback, false /* ignoreClass */, isOverIms);
}
/**
@@ -407,7 +408,7 @@
*/
@VisibleForTesting
public void injectSmsPdu(SmsMessage msg, String format, SmsInjectionCallback callback,
- boolean ignoreClass) {
+ boolean ignoreClass, boolean isOverIms) {
Rlog.d(TAG, "SmsDispatchersController:injectSmsPdu");
try {
if (msg == null) {
@@ -428,11 +429,13 @@
if (format.equals(SmsConstants.FORMAT_3GPP)) {
Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg
+ ", format=" + format + "to mGsmInboundSmsHandler");
- mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
+ mGsmInboundSmsHandler.sendMessage(
+ InboundSmsHandler.EVENT_INJECT_SMS, isOverIms ? 1 : 0, 0, ar);
} else if (format.equals(SmsConstants.FORMAT_3GPP2)) {
Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg
+ ", format=" + format + "to mCdmaInboundSmsHandler");
- mCdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
+ mCdmaInboundSmsHandler.sendMessage(
+ InboundSmsHandler.EVENT_INJECT_SMS, isOverIms ? 1 : 0, 0, ar);
} else {
// Invalid pdu format.
Rlog.e(TAG, "Invalid pdu format: " + format);
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index bfa982e..39a36af 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -350,9 +350,10 @@
*/
public InboundSmsTracker makeInboundSmsTracker(Context context, byte[] pdu, long timestamp,
int destPort, boolean is3gpp2, boolean is3gpp2WapPdu, String address,
- String displayAddr, String messageBody, boolean isClass0, int subId) {
+ String displayAddr, String messageBody, boolean isClass0, int subId,
+ @InboundSmsHandler.SmsSource int smsSource) {
return new InboundSmsTracker(context, pdu, timestamp, destPort, is3gpp2, is3gpp2WapPdu,
- address, displayAddr, messageBody, isClass0, subId);
+ address, displayAddr, messageBody, isClass0, subId, smsSource);
}
/**
@@ -361,10 +362,10 @@
public InboundSmsTracker makeInboundSmsTracker(Context context, byte[] pdu, long timestamp,
int destPort, boolean is3gpp2, String address, String displayAddr, int referenceNumber,
int sequenceNumber, int messageCount, boolean is3gpp2WapPdu, String messageBody,
- boolean isClass0, int subId) {
+ boolean isClass0, int subId, @InboundSmsHandler.SmsSource int smsSource) {
return new InboundSmsTracker(context, pdu, timestamp, destPort, is3gpp2, address,
displayAddr, referenceNumber, sequenceNumber, messageCount, is3gpp2WapPdu,
- messageBody, isClass0, subId);
+ messageBody, isClass0, subId, smsSource);
}
/**
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
index 61377dc..1597b8a 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
@@ -188,10 +188,11 @@
* Process Cell Broadcast, Voicemail Notification, and other 3GPP/3GPP2-specific messages.
*
* @param smsb the SmsMessageBase object from the RIL
+ * @param smsSource the source of the SMS message
* @return true if the message was handled here; false to continue processing
*/
@Override
- protected int dispatchMessageRadioSpecific(SmsMessageBase smsb) {
+ protected int dispatchMessageRadioSpecific(SmsMessageBase smsb, @SmsSource int smsSource) {
SmsMessage sms = (SmsMessage) smsb;
boolean isBroadcastType = (SmsEnvelope.MESSAGE_TYPE_BROADCAST == sms.getMessageType());
@@ -217,7 +218,7 @@
case SmsEnvelope.TELESERVICE_VMN:
case SmsEnvelope.TELESERVICE_MWI:
// handle voicemail indication
- handleVoicemailTeleservice(sms);
+ handleVoicemailTeleservice(sms, smsSource);
return Intents.RESULT_SMS_HANDLED;
case SmsEnvelope.TELESERVICE_WMT:
@@ -258,10 +259,10 @@
if (SmsEnvelope.TELESERVICE_WAP == teleService) {
return processCdmaWapPdu(sms.getUserData(), sms.mMessageRef,
sms.getOriginatingAddress(), sms.getDisplayOriginatingAddress(),
- sms.getTimestampMillis());
+ sms.getTimestampMillis(), smsSource);
}
- return dispatchNormalMessage(smsb);
+ return dispatchNormalMessage(smsb, smsSource);
}
/**
@@ -309,7 +310,7 @@
*
* @param sms the message to process
*/
- private void handleVoicemailTeleservice(SmsMessage sms) {
+ private void handleVoicemailTeleservice(SmsMessage sms, @SmsSource int smsSource) {
int voicemailCount = sms.getNumOfVoicemails();
if (DBG) log("Voicemail count=" + voicemailCount);
@@ -324,7 +325,7 @@
// update voice mail count in phone
mPhone.setVoiceMessageCount(voicemailCount);
// update metrics
- addVoicemailSmsToMetrics();
+ addVoicemailSmsToMetrics(smsSource);
}
/**
@@ -338,7 +339,7 @@
* to applications
*/
private int processCdmaWapPdu(byte[] pdu, int referenceNumber, String address, String dispAddr,
- long timestamp) {
+ long timestamp, @SmsSource int smsSource) {
int index = 0;
int msgType = (0xFF & pdu[index++]);
@@ -386,7 +387,8 @@
referenceNumber,
segment, totalSegments, true, HexDump.toHexString(userData),
false /* isClass0 */,
- mPhone.getSubId());
+ mPhone.getSubId(),
+ smsSource);
// de-duping is done only for text messages
return addTrackerToRawTableAndSendMessage(tracker, false /* don't de-dup */);
@@ -431,9 +433,10 @@
/**
* Add voicemail indication SMS 0 to metrics.
*/
- private void addVoicemailSmsToMetrics() {
+ private void addVoicemailSmsToMetrics(@SmsSource int smsSource) {
mMetrics.writeIncomingVoiceMailSms(mPhone.getPhoneId(),
android.telephony.SmsMessage.FORMAT_3GPP2);
+ mPhone.getSmsStats().onIncomingSmsVoicemail(true /* is3gpp2 */, smsSource);
}
/**
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index a1e4b91..5b4c85c 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -87,6 +87,7 @@
import com.android.internal.telephony.TelephonyStatsLog;
import com.android.internal.telephony.dataconnection.DcTracker.ReleaseNetworkType;
import com.android.internal.telephony.dataconnection.DcTracker.RequestNetworkType;
+import com.android.internal.telephony.metrics.DataCallSessionStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
import com.android.internal.util.AsyncChannel;
@@ -198,6 +199,9 @@
private int[] mAdministratorUids = new int[0];
+ // stats per data call
+ private DataCallSessionStats mDataCallSessionStats;
+
/**
* Used internally for saving connecting parameters.
*/
@@ -692,6 +696,7 @@
mCid = -1;
mDataRegState = mPhone.getServiceState().getDataRegistrationState();
mIsSuspended = false;
+ mDataCallSessionStats = new DataCallSessionStats(mPhone);
int networkType = getNetworkType();
mRilRat = ServiceState.networkTypeToRilRadioTechnology(networkType);
@@ -1017,6 +1022,7 @@
if (apnContext != null) apnContext.requestLog(str);
mDataServiceManager.deactivateDataCall(mCid, discReason,
obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
+ mDataCallSessionStats.setDeactivateDataCallReason(discReason);
}
private void notifyAllWithEvent(ApnContext alreadySent, int event, String reason) {
@@ -1975,6 +1981,8 @@
if (DBG) log("DcDefaultState EVENT_TEAR_DOWN_NOW");
mDataServiceManager.deactivateDataCall(mCid, DataService.REQUEST_REASON_NORMAL,
null);
+ mDataCallSessionStats.setDeactivateDataCallReason(
+ DataService.REQUEST_REASON_NORMAL);
break;
case EVENT_LOST_CONNECTION:
if (DBG) {
@@ -1997,6 +2005,10 @@
+ " drs=" + mDataRegState
+ " mRilRat=" + mRilRat);
}
+ // this is for DRS or RAT changes, so only call onRatChanged if RAT is changed
+ if (mRilRat != 0) {
+ mDataCallSessionStats.onRatChanged(mRilRat);
+ }
break;
case EVENT_START_HANDOVER: //calls startHandover()
@@ -2249,6 +2261,7 @@
.registerCarrierPrivilegesListener(
getHandler(), EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED, null);
notifyDataConnectionState();
+ mDataCallSessionStats.onSetupDataCall(mApnSetting.getApnTypeBitmask());
}
@Override
public boolean processMessage(Message msg) {
@@ -2317,8 +2330,10 @@
} else if (delay >= 0) {
retryTime = SystemClock.elapsedRealtime() + delay;
}
+ int newRequestType = DcTracker.calculateNewRetryRequestType(
+ mHandoverFailureMode, cp.mRequestType, mDcFailCause);
mDct.getDataThrottler().setRetryTime(mApnSetting.getApnTypeBitmask(),
- retryTime, dataCallResponse.getHandoverFailureMode());
+ retryTime, newRequestType);
String str = "DcActivatingState: ERROR_DATA_SERVICE_SPECIFIC_ERROR "
+ " delay=" + delay
@@ -2346,6 +2361,10 @@
throw new RuntimeException("Unknown SetupResult, should not happen");
}
retVal = HANDLED;
+ mDataCallSessionStats
+ .onSetupDataCallResponse(dataCallResponse, cp.mRilRat,
+ mApnSetting.getApnTypeBitmask(), mApnSetting.getProtocol(),
+ result.mFailCause);
break;
case EVENT_CARRIER_PRIVILEGED_UIDS_CHANGED:
AsyncResult asyncResult = (AsyncResult) msg.obj;
@@ -2498,8 +2517,9 @@
getHandler(), DataConnection.EVENT_LINK_CAPACITY_CHANGED, null);
}
notifyDataConnectionState();
+ int apnBitMask = mApnSetting.getApnTypeBitmask();
TelephonyMetrics.getInstance().writeRilDataCallEvent(mPhone.getPhoneId(),
- mCid, mApnSetting.getApnTypeBitmask(), RilDataCall.State.CONNECTED);
+ mCid, apnBitMask, RilDataCall.State.CONNECTED);
}
@Override
@@ -2527,6 +2547,7 @@
TelephonyMetrics.getInstance().writeRilDataCallEvent(mPhone.getPhoneId(),
mCid, mApnSetting.getApnTypeBitmask(), RilDataCall.State.DISCONNECTED);
+ mDataCallSessionStats.onDataCallDisconnected(mCid);
mPhone.getCarrierPrivilegesTracker().unregisterCarrierPrivilegesListener(getHandler());
}
@@ -2638,6 +2659,10 @@
mNetworkAgent.sendLinkProperties(mLinkProperties, DataConnection.this);
}
retVal = HANDLED;
+ // this is for DRS or RAT changes, so only call onRatChanged if RAT is changed
+ if (mRilRat != 0) {
+ mDataCallSessionStats.onRatChanged(mRilRat);
+ }
break;
}
case EVENT_NR_FREQUENCY_CHANGED:
@@ -3175,6 +3200,12 @@
}
}
+ /** Sets the {@link DataCallSessionStats} mock for this phone ID during unit testing. */
+ @VisibleForTesting
+ public void setDataCallSessionStats(DataCallSessionStats dataCallSessionStats) {
+ mDataCallSessionStats = dataCallSessionStats;
+ }
+
/**
* @return the string for msg.what as our info.
*/
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java b/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java
index da5b4a1..60e1b58 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataThrottler.java
@@ -22,7 +22,6 @@
import android.telephony.Annotation.ApnType;
import android.telephony.data.ApnSetting;
import android.telephony.data.ApnThrottleStatus;
-import android.telephony.data.DataCallResponse;
import com.android.internal.telephony.RetryManager;
import com.android.telephony.Rlog;
@@ -67,7 +66,7 @@
* {@link RetryManager#NO_RETRY} indicates retry should never happen.
*/
public void setRetryTime(@ApnType int apnTypes, long retryElapsedTime,
- @DataCallResponse.HandoverFailureMode int handoverFailureMode) {
+ @DcTracker.RequestNetworkType int newRequestType) {
if (retryElapsedTime < 0) {
retryElapsedTime = RetryManager.NO_SUGGESTED_RETRY_DELAY;
}
@@ -79,8 +78,7 @@
int apnType = apnTypes & -apnTypes;
//Update the apn throttle status
- ApnThrottleStatus newStatus =
- createStatus(apnType, retryElapsedTime, handoverFailureMode);
+ ApnThrottleStatus newStatus = createStatus(apnType, retryElapsedTime, newRequestType);
ApnThrottleStatus oldStatus = mApnThrottleStatus.get(apnType);
@@ -130,13 +128,13 @@
}
private ApnThrottleStatus createStatus(@Annotation.ApnType int apnType, long retryElapsedTime,
- @DataCallResponse.HandoverFailureMode int handoverFailureMode) {
+ @DcTracker.RequestNetworkType int newRequestType) {
ApnThrottleStatus.Builder builder = new ApnThrottleStatus.Builder();
if (retryElapsedTime == RetryManager.NO_SUGGESTED_RETRY_DELAY) {
builder
.setNoThrottle()
- .setRetryType(getRetryType(handoverFailureMode));
+ .setRetryType(getRetryType(newRequestType));
} else if (retryElapsedTime == RetryManager.NO_RETRY) {
builder
.setThrottleExpiryTimeMillis(RetryManager.NO_RETRY)
@@ -144,7 +142,7 @@
} else {
builder
.setThrottleExpiryTimeMillis(retryElapsedTime)
- .setRetryType(getRetryType(handoverFailureMode));
+ .setRetryType(getRetryType(newRequestType));
}
return builder
.setSlotIndex(mSlotIndex)
@@ -153,18 +151,17 @@
.build();
}
- private static int getRetryType(@DataCallResponse.HandoverFailureMode int handoverFailureMode) {
- int retryType;
- int requestType = DcTracker.calcRequestType(handoverFailureMode);
- if (requestType == DcTracker.REQUEST_TYPE_NORMAL) {
- retryType = ApnThrottleStatus.RETRY_TYPE_NEW_CONNECTION;
- } else if (requestType == DcTracker.REQUEST_TYPE_HANDOVER) {
- retryType = ApnThrottleStatus.RETRY_TYPE_HANDOVER;
- } else {
- loge("createStatus: Unknown requestType=" + requestType);
- retryType = ApnThrottleStatus.RETRY_TYPE_NEW_CONNECTION;
+ private static int getRetryType(@DcTracker.RequestNetworkType int newRequestType) {
+ if (newRequestType == DcTracker.REQUEST_TYPE_NORMAL) {
+ return ApnThrottleStatus.RETRY_TYPE_NEW_CONNECTION;
}
- return retryType;
+
+ if (newRequestType == DcTracker.REQUEST_TYPE_HANDOVER) {
+ return ApnThrottleStatus.RETRY_TYPE_HANDOVER;
+ }
+
+ loge("createStatus: Unknown requestType=" + newRequestType);
+ return ApnThrottleStatus.RETRY_TYPE_NEW_CONNECTION;
}
private void sendApnThrottleStatusChanged(List<ApnThrottleStatus> statuses) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index a09a0f6..d3011c7 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -24,6 +24,7 @@
import static android.telephony.data.ApnSetting.TYPE_IA;
import static android.telephony.data.DataCallResponse.HANDOVER_FAILURE_MODE_DO_FALLBACK;
import static android.telephony.data.DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY;
+import static android.telephony.data.DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL;
import static com.android.internal.telephony.RILConstants.DATA_PROFILE_DEFAULT;
import static com.android.internal.telephony.RILConstants.DATA_PROFILE_INVALID;
@@ -114,6 +115,7 @@
import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataAllowedReasonType;
import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
import com.android.internal.telephony.dataconnection.DataEnabledSettings.DataEnabledChangedReason;
+import com.android.internal.telephony.metrics.DataStallRecoveryStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.util.ArrayUtils;
import com.android.internal.telephony.util.TelephonyUtils;
@@ -340,6 +342,9 @@
private boolean mNrSaSub6Unmetered = false;
private boolean mRoamingUnmetered = false;
+ // stats per data call recovery event
+ private DataStallRecoveryStats mDataStallRecoveryStats;
+
/* List of SubscriptionPlans, updated when initialized and when plans are changed. */
private List<SubscriptionPlan> mSubscriptionPlans = null;
@@ -534,7 +539,8 @@
if (DBG) log("onDataReconnect: keep associated");
}
// TODO: IF already associated should we send the EVENT_TRY_SETUP_DATA???
- sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, apnContext));
+ sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, requestType,
+ 0, apnContext));
}
}
@@ -2421,7 +2427,7 @@
if (ac != null) {
@ApnType int apnTypes = ac.getApnTypeBitmask();
mDataThrottler.setRetryTime(apnTypes, RetryManager.NO_SUGGESTED_RETRY_DELAY,
- DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL);
+ REQUEST_TYPE_NORMAL);
} else {
loge("EVENT_APN_UNTHROTTLED: Invalid APN passed: " + apn);
}
@@ -2496,20 +2502,19 @@
private void sendRequestNetworkCompleteMsg(Message message, boolean success,
@TransportType int transport,
@RequestNetworkType int requestType,
- @HandoverFailureMode int handoverFailureMode,
- @DataFailureCause int cause) {
+ boolean doFallbackOnFailedHandover) {
if (message == null) return;
Bundle b = message.getData();
b.putBoolean(DATA_COMPLETE_MSG_EXTRA_SUCCESS, success);
b.putInt(DATA_COMPLETE_MSG_EXTRA_REQUEST_TYPE, requestType);
b.putInt(DATA_COMPLETE_MSG_EXTRA_TRANSPORT_TYPE, transport);
- b.putBoolean(DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK,
- shouldFallbackOnFailedHandover(handoverFailureMode, requestType, cause));
+ b.putBoolean(DATA_COMPLETE_MSG_EXTRA_HANDOVER_FAILURE_FALLBACK, doFallbackOnFailedHandover);
message.sendToTarget();
}
- private boolean shouldFallbackOnFailedHandover(@HandoverFailureMode int handoverFailureMode,
+ private static boolean shouldFallbackOnFailedHandover(
+ @HandoverFailureMode int handoverFailureMode,
@RequestNetworkType int requestType,
@DataFailureCause int cause) {
if (requestType != REQUEST_TYPE_HANDOVER) {
@@ -2524,6 +2529,33 @@
}
}
+ /**
+ * Calculates the new request type that will be used the next time a data connection retries
+ * after a failed data call attempt.
+ */
+ @RequestNetworkType
+ public static int calculateNewRetryRequestType(@HandoverFailureMode int handoverFailureMode,
+ @RequestNetworkType int requestType,
+ @DataFailureCause int cause) {
+ boolean fallbackOnFailedHandover =
+ shouldFallbackOnFailedHandover(handoverFailureMode, requestType, cause);
+ if (requestType != REQUEST_TYPE_HANDOVER) {
+ //The fallback is only relevant if the request is a handover
+ return requestType;
+ }
+
+ if (fallbackOnFailedHandover) {
+ // Since fallback is happening, the request type is really "NONE".
+ return REQUEST_TYPE_NORMAL;
+ }
+
+ if (handoverFailureMode == HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL) {
+ return REQUEST_TYPE_NORMAL;
+ }
+
+ return REQUEST_TYPE_HANDOVER;
+ }
+
public void enableApn(@ApnType int apnType, @RequestNetworkType int requestType,
Message onCompleteMsg) {
sendMessage(obtainMessage(DctConstants.EVENT_ENABLE_APN, apnType, requestType,
@@ -2536,7 +2568,7 @@
if (apnContext == null) {
loge("onEnableApn(" + apnType + "): NO ApnContext");
sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType, requestType,
- DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, DataFailCause.NONE);
+ false);
return;
}
@@ -2552,7 +2584,7 @@
if (DBG) log(str);
apnContext.requestLog(str);
sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType, requestType,
- DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN, DataFailCause.NONE);
+ false);
return;
}
@@ -2568,15 +2600,13 @@
if (DBG) log("onEnableApn: 'CONNECTED' so return");
// Don't add to local log since this is so common
sendRequestNetworkCompleteMsg(onCompleteMsg, true, mTransportType,
- requestType, DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN,
- DataFailCause.NONE);
+ requestType, false);
return;
case DISCONNECTING:
if (DBG) log("onEnableApn: 'DISCONNECTING' so return");
apnContext.requestLog("onEnableApn state=DISCONNECTING, so return");
sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType,
- requestType, DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN,
- DataFailCause.NONE);
+ requestType, false);
return;
case IDLE:
// fall through: this is unexpected but if it happens cleanup and try setup
@@ -2605,8 +2635,7 @@
addRequestNetworkCompleteMsg(onCompleteMsg, apnType);
} else {
sendRequestNetworkCompleteMsg(onCompleteMsg, false, mTransportType,
- requestType, DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN,
- DataFailCause.NONE);
+ requestType, false);
}
} else {
log("onEnableApn: config not ready yet.");
@@ -2879,6 +2908,9 @@
protected void onDataSetupComplete(ApnContext apnContext, boolean success,
@DataFailureCause int cause, @RequestNetworkType int requestType,
@HandoverFailureMode int handoverFailureMode) {
+ boolean fallbackOnFailedHandover = shouldFallbackOnFailedHandover(
+ handoverFailureMode, requestType, cause);
+
if (success && (handoverFailureMode != DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN
&& handoverFailureMode != DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY)) {
Log.wtf(mLogTag, "bad failure mode: "
@@ -2890,7 +2922,7 @@
if (messageList != null) {
for (Message msg : messageList) {
sendRequestNetworkCompleteMsg(msg, success, mTransportType, requestType,
- handoverFailureMode, cause);
+ fallbackOnFailedHandover);
}
messageList.clear();
}
@@ -3044,22 +3076,13 @@
apnContext.markApnPermanentFailed(apn);
}
- requestType = calcRequestType(handoverFailureMode);
- onDataSetupCompleteError(apnContext, requestType,
- shouldFallbackOnFailedHandover(handoverFailureMode, requestType, cause));
+ int newRequestType = calculateNewRetryRequestType(handoverFailureMode, requestType,
+ cause);
+ onDataSetupCompleteError(apnContext, newRequestType, fallbackOnFailedHandover);
}
}
- /**
- * Converts the handover failure mode to the corresponding request network type.
- */
- @RequestNetworkType
- public static int calcRequestType(
- @HandoverFailureMode int handoverFailureMode) {
- return (handoverFailureMode
- == DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER)
- ? REQUEST_TYPE_HANDOVER : REQUEST_TYPE_NORMAL;
- }
+
/**
* Error has occurred during the SETUP {aka bringUP} request and the DCT
@@ -3068,11 +3091,10 @@
* be a delay defined by {@link ApnContext#getDelayForNextApn(boolean)}.
*/
protected void onDataSetupCompleteError(ApnContext apnContext,
- @RequestNetworkType int requestType, boolean fallback) {
+ @RequestNetworkType int requestType, boolean fallbackOnFailedHandover) {
long delay = apnContext.getDelayForNextApn(mFailFast);
-
// Check if we need to retry or not.
- if (delay >= 0 && delay != RetryManager.NO_RETRY && !fallback) {
+ if (delay >= 0 && delay != RetryManager.NO_RETRY && !fallbackOnFailedHandover) {
if (DBG) {
log("onDataSetupCompleteError: APN type=" + apnContext.getApnType()
+ ". Request type=" + requestTypeToString(requestType) + ", Retry in "
@@ -3652,7 +3674,7 @@
break;
case DctConstants.EVENT_TRY_SETUP_DATA:
- trySetupData((ApnContext) msg.obj, REQUEST_TYPE_NORMAL);
+ trySetupData((ApnContext) msg.obj, msg.arg1);
break;
case DctConstants.EVENT_CLEAN_UP_CONNECTION:
@@ -4704,7 +4726,7 @@
RECOVERY_ACTION_RADIO_RESTART
})
@Retention(RetentionPolicy.SOURCE)
- private @interface RecoveryAction {};
+ public @interface RecoveryAction {};
private static final int RECOVERY_ACTION_GET_DATA_CALL_LIST = 0;
private static final int RECOVERY_ACTION_CLEANUP = 1;
private static final int RECOVERY_ACTION_REREGISTER = 2;
@@ -4806,6 +4828,7 @@
mPhone.getPhoneId(), signalStrength);
TelephonyMetrics.getInstance().writeDataStallEvent(
mPhone.getPhoneId(), recoveryAction);
+ DataStallRecoveryStats.onDataStallEvent(recoveryAction, mPhone);
broadcastDataStallDetected(recoveryAction);
switch (recoveryAction) {
diff --git a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
index 7af9ea3..334a537 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
@@ -153,11 +153,12 @@
* are handled by {@link #dispatchNormalMessage} in parent class.
*
* @param smsb the SmsMessageBase object from the RIL
+ * @param smsSource the source of the SMS message
* @return a result code from {@link android.provider.Telephony.Sms.Intents},
* or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC
*/
@Override
- protected int dispatchMessageRadioSpecific(SmsMessageBase smsb) {
+ protected int dispatchMessageRadioSpecific(SmsMessageBase smsb, @SmsSource int smsSource) {
SmsMessage sms = (SmsMessage) smsb;
if (sms.isTypeZero()) {
@@ -174,14 +175,14 @@
// As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should not be
// Displayed/Stored/Notified. They should only be acknowledged.
log("Received short message type 0, Don't display or store it. Send Ack");
- addSmsTypeZeroToMetrics();
+ addSmsTypeZeroToMetrics(smsSource);
return Intents.RESULT_SMS_HANDLED;
}
// Send SMS-PP data download messages to UICC. See 3GPP TS 31.111 section 7.1.1.
if (sms.isUsimDataDownload()) {
UsimServiceTable ust = mPhone.getUsimServiceTable();
- return mDataDownloadHandler.handleUsimDataDownload(ust, sms);
+ return mDataDownloadHandler.handleUsimDataDownload(ust, sms, smsSource);
}
boolean handled = false;
@@ -195,7 +196,7 @@
if (DBG) log("Received voice mail indicator clear SMS shouldStore=" + !handled);
}
if (handled) {
- addVoicemailSmsToMetrics();
+ addVoicemailSmsToMetrics(smsSource);
return Intents.RESULT_SMS_HANDLED;
}
@@ -206,7 +207,7 @@
return Intents.RESULT_SMS_OUT_OF_MEMORY;
}
- return dispatchNormalMessage(smsb);
+ return dispatchNormalMessage(smsb, smsSource);
}
private void updateMessageWaitingIndicator(int voicemailCount) {
@@ -258,16 +259,18 @@
/**
* Add SMS of type 0 to metrics.
*/
- private void addSmsTypeZeroToMetrics() {
+ private void addSmsTypeZeroToMetrics(@SmsSource int smsSource) {
mMetrics.writeIncomingSmsTypeZero(mPhone.getPhoneId(),
android.telephony.SmsMessage.FORMAT_3GPP);
+ mPhone.getSmsStats().onIncomingSmsTypeZero(smsSource);
}
/**
* Add voicemail indication SMS 0 to metrics.
*/
- private void addVoicemailSmsToMetrics() {
+ private void addVoicemailSmsToMetrics(@SmsSource int smsSource) {
mMetrics.writeIncomingVoiceMailSms(mPhone.getPhoneId(),
android.telephony.SmsMessage.FORMAT_3GPP);
+ mPhone.getSmsStats().onIncomingSmsVoicemail(false /* is3gpp2 */, smsSource);
}
}
diff --git a/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java b/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java
index 5d08846..ed819c1 100644
--- a/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java
+++ b/src/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java
@@ -25,6 +25,8 @@
import android.telephony.SmsManager;
import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.InboundSmsHandler;
+import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.cat.ComprehensionTlvTag;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.uicc.IccIoResult;
@@ -73,9 +75,11 @@
*
* @param ust the UsimServiceTable, to check if data download is enabled
* @param smsMessage the SMS message to process
+ * @param smsSource the source of the SMS message
* @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure
*/
- int handleUsimDataDownload(UsimServiceTable ust, SmsMessage smsMessage) {
+ int handleUsimDataDownload(UsimServiceTable ust, SmsMessage smsMessage,
+ @InboundSmsHandler.SmsSource int smsSource) {
// If we receive an SMS-PP message before the UsimServiceTable has been loaded,
// assume that the data download service is not present. This is very unlikely to
// happen because the IMS connection will not be established until after the ISIM
@@ -83,7 +87,7 @@
if (ust != null && ust.isAvailable(
UsimServiceTable.UsimService.DATA_DL_VIA_SMS_PP)) {
Rlog.d(TAG, "Received SMS-PP data download, sending to UICC.");
- return startDataDownload(smsMessage);
+ return startDataDownload(smsMessage, smsSource);
} else {
Rlog.d(TAG, "DATA_DL_VIA_SMS_PP service not available, storing message to UICC.");
String smsc = IccUtils.bytesToHexString(
@@ -92,7 +96,7 @@
mCi.writeSmsToSim(SmsManager.STATUS_ON_ICC_UNREAD, smsc,
IccUtils.bytesToHexString(smsMessage.getPdu()),
obtainMessage(EVENT_WRITE_SMS_COMPLETE));
- addUsimDataDownloadToMetrics(false);
+ addUsimDataDownloadToMetrics(false, smsSource);
return Activity.RESULT_OK; // acknowledge after response from write to USIM
}
@@ -103,10 +107,13 @@
* thread than this Handler is running on.
*
* @param smsMessage the message to process
+ * @param smsSource the source of the SMS message
* @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure
*/
- public int startDataDownload(SmsMessage smsMessage) {
- if (sendMessage(obtainMessage(EVENT_START_DATA_DOWNLOAD, smsMessage))) {
+ public int startDataDownload(SmsMessage smsMessage,
+ @InboundSmsHandler.SmsSource int smsSource) {
+ if (sendMessage(obtainMessage(EVENT_START_DATA_DOWNLOAD,
+ smsSource, 0 /* unused */, smsMessage))) {
return Activity.RESULT_OK; // we will send SMS ACK/ERROR based on UICC response
} else {
Rlog.e(TAG, "startDataDownload failed to send message to start data download.");
@@ -114,7 +121,8 @@
}
}
- private void handleDataDownload(SmsMessage smsMessage) {
+ private void handleDataDownload(SmsMessage smsMessage,
+ @InboundSmsHandler.SmsSource int smsSource) {
int dcs = smsMessage.getDataCodingScheme();
int pid = smsMessage.getProtocolIdentifier();
byte[] pdu = smsMessage.getPdu(); // includes SC address
@@ -166,7 +174,7 @@
if (index != envelope.length) {
Rlog.e(TAG, "startDataDownload() calculated incorrect envelope length, aborting.");
acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR);
- addUsimDataDownloadToMetrics(false);
+ addUsimDataDownloadToMetrics(false, smsSource);
return;
}
@@ -174,7 +182,7 @@
mCi.sendEnvelopeWithStatus(encodedEnvelope, obtainMessage(
EVENT_SEND_ENVELOPE_RESPONSE, new int[]{ dcs, pid }));
- addUsimDataDownloadToMetrics(true);
+ addUsimDataDownloadToMetrics(true, smsSource);
}
/**
@@ -284,9 +292,11 @@
* to the USIM. The metrics does not cover the case where the SMS-PP might be rejected
* by the USIM itself.
*/
- private void addUsimDataDownloadToMetrics(boolean result) {
+ private void addUsimDataDownloadToMetrics(boolean result,
+ @InboundSmsHandler.SmsSource int smsSource) {
TelephonyMetrics metrics = TelephonyMetrics.getInstance();
metrics.writeIncomingSMSPP(mPhoneId, android.telephony.SmsMessage.FORMAT_3GPP, result);
+ PhoneFactory.getPhone(mPhoneId).getSmsStats().onIncomingSmsPP(smsSource, result);
}
/**
@@ -300,7 +310,7 @@
switch (msg.what) {
case EVENT_START_DATA_DOWNLOAD:
- handleDataDownload((SmsMessage) msg.obj);
+ handleDataDownload((SmsMessage) msg.obj, msg.arg1 /* smsSource */);
break;
case EVENT_SEND_ENVELOPE_RESPONSE:
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index a6daca4..dca68a0 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -109,6 +109,7 @@
import com.android.internal.telephony.dataconnection.TransportManager;
import com.android.internal.telephony.emergency.EmergencyNumberTracker;
import com.android.internal.telephony.gsm.SuppServiceNotification;
+import com.android.internal.telephony.metrics.ImsStats;
import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.metrics.VoiceCallSessionStats;
import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
@@ -268,6 +269,8 @@
// List of Registrants to send supplementary service notifications to.
private RegistrantList mSsnRegistrants = new RegistrantList();
+ private ImsStats mImsStats;
+
// A runnable which is used to automatically exit from Ecm after a period of time.
private Runnable mExitEcmRunnable = new Runnable() {
@Override
@@ -423,6 +426,7 @@
mDefaultPhone = defaultPhone;
mImsManagerFactory = imsManagerFactory;
mImsPhoneSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
+ mImsStats = new ImsStats(this);
// The ImsExternalCallTracker needs to be defined before the ImsPhoneCallTracker, as the
// ImsPhoneCallTracker uses a thread to spool up the ImsManager. Part of this involves
// setting the multiendpoint listener on the external call tracker. So we need to ensure
@@ -2390,6 +2394,7 @@
+ AccessNetworkConstants.transportTypeToString(imsRadioTech));
setServiceState(ServiceState.STATE_IN_SERVICE);
mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.CONNECTED, null);
+ mImsStats.onImsRegistered(imsRadioTech);
}
@Override
@@ -2403,6 +2408,7 @@
setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.PROGRESSING,
null);
+ mImsStats.onImsRegistering(imsRadioTech);
}
@Override
@@ -2413,6 +2419,7 @@
processDisconnectReason(imsReasonInfo);
mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.DISCONNECTED,
imsReasonInfo);
+ mImsStats.onImsUnregistered(imsReasonInfo);
}
@Override
@@ -2450,6 +2457,17 @@
return mDefaultPhone.getVoiceCallSessionStats();
}
+ /** Returns the {@link ImsStats} for this IMS phone. */
+ public ImsStats getImsStats() {
+ return mImsStats;
+ }
+
+ /** Sets the {@link ImsStats} mock for this IMS phone during unit testing. */
+ @VisibleForTesting
+ public void setImsStats(ImsStats imsStats) {
+ mImsStats = imsStats;
+ }
+
public boolean hasAliveCall() {
return (getForegroundCall().getState() != Call.State.IDLE ||
getBackgroundCall().getState() != Call.State.IDLE);
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index 81f11a8..a6a6514 100755
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -3684,6 +3684,7 @@
ImsPhoneConnection conn = findConnection(imsCall);
if (conn != null) {
conn.updateMultipartyState(isMultiParty);
+ mPhone.getVoiceCallSessionStats().onMultipartyChange(conn, isMultiParty);
}
}
@@ -3823,6 +3824,7 @@
@Override
public void onSetFeatureResponse(int feature, int network, int value, int status) {
mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(), feature, network, value);
+ mPhone.getImsStats().onSetFeatureResponse(feature, network, value);
}
@Override
@@ -4939,8 +4941,9 @@
mPhone.onFeatureCapabilityChanged();
- mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), getImsRegistrationTech(),
- mMmTelCapabilities);
+ int regTech = getImsRegistrationTech();
+ mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), regTech, mMmTelCapabilities);
+ mPhone.getImsStats().onImsCapabilitiesChanged(regTech, mMmTelCapabilities);
}
@VisibleForTesting
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 83b5ed2..76604a5 100755
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -1106,6 +1106,7 @@
mImsVideoCallProviderWrapper.onVideoStateChanged(newVideoState);
}
setVideoState(newVideoState);
+ mOwner.getPhone().getVoiceCallSessionStats().onVideoStateChange(this, newVideoState);
}
diff --git a/src/java/com/android/internal/telephony/metrics/AirplaneModeStats.java b/src/java/com/android/internal/telephony/metrics/AirplaneModeStats.java
new file mode 100644
index 0000000..1689437
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/AirplaneModeStats.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 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.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import static com.android.internal.telephony.TelephonyStatsLog.AIRPLANE_MODE;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.telephony.SubscriptionManager;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.telephony.Rlog;
+
+/** Metrics for the usage of airplane mode. */
+public class AirplaneModeStats extends ContentObserver {
+ private static final String TAG = AirplaneModeStats.class.getSimpleName();
+
+ /** Ignore airplane mode events occurring in the first 30 seconds. */
+ private static final long GRACE_PERIOD_MILLIS = 30000L;
+
+ /** An airplane mode toggle is considered short if under 10 seconds. */
+ private static final long SHORT_TOGGLE_MILLIS = 10000L;
+
+ private long mLastActivationTime = 0L;
+
+ private final Context mContext;
+ private final Uri mAirplaneModeSettingUri;
+
+ public AirplaneModeStats(Context context) {
+ super(new Handler(Looper.getMainLooper()));
+
+ mContext = context;
+ mAirplaneModeSettingUri = Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON);
+
+ context.getContentResolver().registerContentObserver(mAirplaneModeSettingUri, false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (uri.equals(mAirplaneModeSettingUri)) {
+ onAirplaneModeChanged(isAirplaneModeOn());
+ }
+ }
+
+ private boolean isAirplaneModeOn() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+ }
+
+ /** Generate metrics when airplane mode is enabled or disabled. */
+ private void onAirplaneModeChanged(boolean isAirplaneModeOn) {
+ Rlog.d(TAG, "Airplane mode change. Value: " + isAirplaneModeOn);
+ long currentTime = SystemClock.elapsedRealtime();
+ if (currentTime < GRACE_PERIOD_MILLIS) {
+ return;
+ }
+
+ boolean isShortToggle = calculateShortToggle(currentTime, isAirplaneModeOn);
+ int carrierId = getCarrierId();
+
+ Rlog.d(TAG, "Airplane mode: " + isAirplaneModeOn + ", short=" + isShortToggle
+ + ", carrierId=" + carrierId);
+ TelephonyStatsLog.write(AIRPLANE_MODE, isAirplaneModeOn, isShortToggle, carrierId);
+ }
+
+
+ /* Keep tracks of time and returns if it was a short toggle. */
+ private boolean calculateShortToggle(long currentTime, boolean isAirplaneModeOn) {
+ boolean isShortToggle = false;
+ if (isAirplaneModeOn) {
+ // When airplane mode is enabled, track the time.
+ if (mLastActivationTime == 0L) {
+ mLastActivationTime = currentTime;
+ }
+ return false;
+ } else {
+ // When airplane mode is disabled, reset the time and check if it was a short toggle.
+ long duration = currentTime - mLastActivationTime;
+ mLastActivationTime = 0L;
+ return duration > 0 && duration < SHORT_TOGGLE_MILLIS;
+ }
+ }
+
+ /**
+ * Returns the carrier ID of the active data subscription. If this is not available,
+ * it returns the carrier ID of the first phone.
+ */
+ private static int getCarrierId() {
+ int dataSubId = SubscriptionManager.getActiveDataSubscriptionId();
+ int phoneId = dataSubId != INVALID_SUBSCRIPTION_ID
+ ? SubscriptionManager.getPhoneId(dataSubId) : 0;
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ return phone != null ? phone.getCarrierId() : INVALID_SUBSCRIPTION_ID;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java b/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java
new file mode 100644
index 0000000..4d0397c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 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 com.android.internal.telephony.TelephonyStatsLog.CARRIER_ID_MISMATCH_REPORTED;
+import static com.android.internal.telephony.TelephonyStatsLog.CARRIER_ID_TABLE_UPDATED;
+
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierIdMismatch;
+import com.android.telephony.Rlog;
+
+/** Metrics for the carrier id matching. */
+public class CarrierIdMatchStats {
+ private static final String TAG = CarrierIdMatchStats.class.getSimpleName();
+
+ private CarrierIdMatchStats() { }
+
+ /** Generate metrics when carrier ID mismatch occurs. */
+ public static void onCarrierIdMismatch(
+ int cid, String mccMnc, String gid1, String spn, String pnn) {
+ PersistAtomsStorage storage = PhoneFactory.getMetricsCollector().getAtomsStorage();
+
+ CarrierIdMismatch carrierIdMismatch = new CarrierIdMismatch();
+ carrierIdMismatch.mccMnc = nullToEmpty(mccMnc);
+ carrierIdMismatch.gid1 = nullToEmpty(gid1);
+ carrierIdMismatch.spn = nullToEmpty(spn);
+ carrierIdMismatch.pnn = carrierIdMismatch.spn.isEmpty() ? nullToEmpty(pnn) : "";
+
+ // Add to storage and generate atom only if it was added (new SIM card).
+ boolean isAdded = storage.addCarrierIdMismatch(carrierIdMismatch);
+ if (isAdded) {
+ Rlog.d(TAG, "New carrier ID mismatch event: " + carrierIdMismatch.toString());
+ TelephonyStatsLog.write(CARRIER_ID_MISMATCH_REPORTED, cid, mccMnc, gid1, spn, pnn);
+ }
+ }
+
+ /** Generate metrics for the carrier ID table version. */
+ public static void sendCarrierIdTableVersion(int carrierIdTableVersion) {
+ PersistAtomsStorage storage = PhoneFactory.getMetricsCollector().getAtomsStorage();
+
+ if (storage.setCarrierIdTableVersion(carrierIdTableVersion)) {
+ Rlog.d(TAG, "New carrier ID table version: " + carrierIdTableVersion);
+ TelephonyStatsLog.write(CARRIER_ID_TABLE_UPDATED, carrierIdTableVersion);
+ }
+ }
+
+ private static String nullToEmpty(String string) {
+ return string != null ? string : "";
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
new file mode 100644
index 0000000..a846d9d
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2020 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 com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_HANDOVER;
+import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_NORMAL;
+import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_RADIO_OFF;
+import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_UNKNOWN;
+import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__IP_TYPE__APN_PROTOCOL_IPV4;
+
+import android.telephony.Annotation.ApnType;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.DataFailCause;
+import android.telephony.ServiceState;
+import android.telephony.data.ApnSetting.ProtocolType;
+import android.telephony.data.DataCallResponse;
+import android.telephony.data.DataService;
+import android.telephony.data.DataService.DeactivateDataReason;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.nano.PersistAtomsProto.DataCallSession;
+import com.android.telephony.Rlog;
+
+import java.util.Random;
+
+/** Collects data call change events per DcTracker for the pulled atom. */
+public class DataCallSessionStats {
+ private static final String TAG = DataCallSessionStats.class.getSimpleName();
+
+ private final Phone mPhone;
+ private long mStartTime;
+ private boolean mOnRatChangedCalledBeforeSetup = false;
+ DataCallSession mOngoingDataCall;
+
+ private final PersistAtomsStorage mAtomsStorage =
+ PhoneFactory.getMetricsCollector().getAtomsStorage();
+
+ private static final Random RANDOM = new Random();
+
+ public DataCallSessionStats(Phone phone) {
+ mPhone = phone;
+ }
+
+ /** create a new ongoing atom when data cal is set up */
+ public synchronized void onSetupDataCall(@ApnType int apnTypeBitMask) {
+ if (!mOnRatChangedCalledBeforeSetup) {
+ // there shouldn't be an ongoing dataCall here, if that's the case, it means that
+ // deactivateDataCall hasn't been processed properly, so we save the previous atom here
+ // and move on to create a new atom.
+ if (mOngoingDataCall != null) {
+ mOngoingDataCall.failureCause = DataFailCause.UNKNOWN;
+ mOngoingDataCall.durationMinutes =
+ convertMillisToMinutes(System.currentTimeMillis() - mStartTime);
+ mOngoingDataCall.ongoing = false;
+ mAtomsStorage.addDataCallSession(mOngoingDataCall);
+ mOngoingDataCall = null;
+ }
+ mOngoingDataCall = getDefaultProto(apnTypeBitMask);
+ mStartTime = System.currentTimeMillis();
+ } else {
+ // if onRatChanged was called before onSetupDataCall, the atom is already initialized
+ // but apnTypeBitMask is initialized to 0, so we need to update it
+ mOngoingDataCall.apnTypeBitmask = apnTypeBitMask;
+ }
+ mOnRatChangedCalledBeforeSetup = false;
+ }
+
+ /**
+ * update the ongoing dataCall's atom for data call response event
+ * @param response setup Data call response
+ * @param radioTechnology The data call RAT
+ * @param apnTypeBitmask APN type bitmask
+ * @param protocol Data connection protocol
+ * @param failureCause failure cause as per android.telephony.DataFailCause
+ */
+ public synchronized void onSetupDataCallResponse(
+ DataCallResponse response,
+ @ServiceState.RilRadioTechnology int radioTechnology,
+ @ApnType int apnTypeBitmask,
+ @ProtocolType int protocol,
+ int failureCause) {
+ // there should've been another call to initiate the atom,
+ // so this method is being called out of order -> no metric will be logged
+ if (mOngoingDataCall == null) {
+ loge("onSetupDataCallResponse: no DataCallSession atom has been initiated.");
+ return;
+ }
+ mOngoingDataCall.ratAtEnd =
+ ServiceState.rilRadioTechnologyToNetworkType(radioTechnology);
+
+ // only set if apn hasn't been set during setup
+ if (mOngoingDataCall.apnTypeBitmask == 0) {
+ mOngoingDataCall.apnTypeBitmask = apnTypeBitmask;
+ }
+
+ mOngoingDataCall.ipType = protocol;
+ mOngoingDataCall.failureCause = failureCause;
+ if (response != null) {
+ mOngoingDataCall.suggestedRetryMillis =
+ (int) Math.min(response.getRetryDurationMillis(), Integer.MAX_VALUE);
+ if (failureCause != DataFailCause.NONE) {
+ mOngoingDataCall.failureCause = failureCause;
+ mOngoingDataCall.setupFailed = true;
+ // set dataCall as inactive
+ mOngoingDataCall.ongoing = false;
+ // store it only if setup has failed
+ mAtomsStorage.addDataCallSession(mOngoingDataCall);
+ mOngoingDataCall = null;
+ }
+ }
+ }
+
+ /**
+ * update the ongoing dataCall's atom when data call is deactivated
+ *
+ * @param reason Deactivate reason
+ */
+ public void setDeactivateDataCallReason(@DeactivateDataReason int reason) {
+ // there should've been another call to initiate the atom,
+ // so this method is being called out of order -> no metric will be logged
+ if (mOngoingDataCall == null) {
+ loge("onSetupDataCallResponse: no DataCallSession atom has been initiated.");
+ return;
+ }
+ switch (reason) {
+ case DataService.REQUEST_REASON_NORMAL:
+ mOngoingDataCall.deactivateReason =
+ DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_NORMAL;
+ break;
+ case DataService.REQUEST_REASON_SHUTDOWN:
+ mOngoingDataCall.deactivateReason =
+ DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_RADIO_OFF;
+ break;
+ case DataService.REQUEST_REASON_HANDOVER:
+ mOngoingDataCall.deactivateReason =
+ DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_HANDOVER;
+ break;
+ default:
+ mOngoingDataCall.deactivateReason =
+ DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_UNKNOWN;
+ break;
+ }
+
+ mOngoingDataCall.oosAtEnd = getIsOos();
+ }
+
+ /**
+ * store the atom, when DataConnection reaches DISCONNECTED state
+ *
+ * @param cid Context Id, uniquely identifies the call
+ */
+ public void onDataCallDisconnected(int cid) {
+ // there should've been another call to initiate the atom,
+ // so this method is being called out of order -> no atom will be saved
+ if (mOngoingDataCall == null) {
+ loge("onSetupDataCallResponse: no DataCallSession atom has been initiated.");
+ return;
+ }
+ mOngoingDataCall.carrierId = cid;
+ mOngoingDataCall.ongoing = false;
+ mOngoingDataCall.durationMinutes =
+ convertMillisToMinutes(System.currentTimeMillis() - mStartTime);
+ // store for the data call list event, after DataCall is disconnected and entered into
+ // inactive mode
+ mAtomsStorage.addDataCallSession(mOngoingDataCall);
+ mOngoingDataCall = null;
+ }
+
+ /** Updates this RAT when it changes. */
+ public synchronized void onRatChanged(@ServiceState.RilRadioTechnology int radioTechnology) {
+ // if no data call is initiated, or we have a new data call while the last one has ended
+ // because onRatChanged might be called before onSetupDataCall
+ if (mOngoingDataCall == null) {
+ mOngoingDataCall = getDefaultProto(0);
+ mStartTime = System.currentTimeMillis();
+ mOnRatChangedCalledBeforeSetup = true;
+ }
+ @NetworkType int rat = ServiceState.rilRadioTechnologyToNetworkType(radioTechnology);
+ if (mOngoingDataCall.ratAtEnd != rat) {
+ mOngoingDataCall.ratSwitchCount++;
+ mOngoingDataCall.ratAtEnd = rat;
+ }
+ }
+
+ private static long convertMillisToMinutes(long millis) {
+ return Math.round(millis / 60000);
+ }
+
+ /** Creates a proto for a normal {@code DataCallSession} with default values. */
+ private DataCallSession getDefaultProto(@ApnType int apnTypeBitmask) {
+ DataCallSession proto = new DataCallSession();
+ proto.dimension = RANDOM.nextInt();
+ proto.isMultiSim = SimSlotState.isMultiSim();
+ proto.isEsim = SimSlotState.isEsim(mPhone.getPhoneId());
+ proto.apnTypeBitmask = apnTypeBitmask;
+ proto.carrierId = mPhone.getCarrierId();
+ proto.isRoaming = getIsRoaming();
+ proto.oosAtEnd = false;
+ proto.ratSwitchCount = 0L;
+ proto.isOpportunistic = getIsOpportunistic();
+ proto.ipType = DATA_CALL_SESSION__IP_TYPE__APN_PROTOCOL_IPV4;
+ proto.setupFailed = false;
+ proto.failureCause = DataFailCause.NONE;
+ proto.suggestedRetryMillis = 0;
+ proto.deactivateReason = DATA_CALL_SESSION__DEACTIVATE_REASON__DEACTIVATE_REASON_UNKNOWN;
+ proto.durationMinutes = 0;
+ proto.ongoing = true;
+ return proto;
+ }
+
+ private boolean getIsRoaming() {
+ ServiceStateTracker serviceStateTracker = mPhone.getServiceStateTracker();
+ ServiceState serviceState =
+ serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
+ return serviceState != null ? serviceState.getRoaming() : false;
+ }
+
+ private boolean getIsOpportunistic() {
+ SubscriptionController subController = SubscriptionController.getInstance();
+ return subController != null ? subController.isOpportunistic(mPhone.getSubId()) : false;
+ }
+
+ private boolean getIsOos() {
+ ServiceStateTracker serviceStateTracker = mPhone.getServiceStateTracker();
+ ServiceState serviceState =
+ serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
+ return serviceState != null
+ ? serviceState.getDataRegistrationState() == ServiceState.STATE_OUT_OF_SERVICE
+ : false;
+ }
+
+ private void loge(String format, Object... args) {
+ Rlog.e(TAG, "[" + mPhone.getPhoneId() + "]" + String.format(format, args));
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
new file mode 100644
index 0000000..7ee6675
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/DataStallRecoveryStats.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 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 android.telephony.Annotation.NetworkType;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.internal.telephony.dataconnection.DcTracker;
+
+/** Generates metrics related to data stall recovery events per phone ID for the pushed atom. */
+public class DataStallRecoveryStats {
+ /**
+ * Create and push new atom when there is a data stall recovery event
+ *
+ * @param recoveryAction Data stall recovery action
+ * @param phone
+ */
+ public static void onDataStallEvent(@DcTracker.RecoveryAction int recoveryAction,
+ Phone phone) {
+ if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ phone = phone.getDefaultPhone();
+ }
+
+ int carrierId = phone.getCarrierId();
+ int rat = getRat(phone);
+ // the number returned here matches the SignalStrength enum we have
+ int signalStrength = phone.getSignalStrength().getLevel();
+ boolean isOpportunistic = getIsOpportunistic(phone);
+ boolean isMultiSim = SimSlotState.getCurrentState().numActiveSims > 1;
+
+ TelephonyStatsLog.write(TelephonyStatsLog.DATA_STALL_RECOVERY_REPORTED, carrierId, rat,
+ signalStrength, recoveryAction, isOpportunistic, isMultiSim);
+ }
+
+ private static @NetworkType int getRat(Phone phone) {
+ ServiceStateTracker serviceStateTracker = phone.getServiceStateTracker();
+ ServiceState serviceState =
+ serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
+ return serviceState != null ? serviceState.getVoiceNetworkType()
+ : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+
+ private static boolean getIsOpportunistic(Phone phone) {
+ SubscriptionController subController = SubscriptionController.getInstance();
+ return subController != null ? subController.isOpportunistic(phone.getSubId()) : false;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/ImsStats.java b/src/java/com/android/internal/telephony/metrics/ImsStats.java
new file mode 100644
index 0000000..b20c598
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/ImsStats.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2020 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.telephony.ims.RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED;
+import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERED;
+import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERING;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+
+import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.AccessNetworkConstants.TransportType;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.RegistrationManager.ImsRegistrationState;
+import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
+import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability;
+import android.telephony.ims.stub.ImsRegistrationImplBase.ImsRegistrationTech;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
+import com.android.telephony.Rlog;
+
+/** Tracks IMS registration metrics for each phone. */
+public class ImsStats {
+ private static final String TAG = ImsStats.class.getSimpleName();
+
+ /**
+ * Minimal duration of the registration state.
+ *
+ * <p>Registration state (including changes in capable/available features) with duration shorter
+ * than this will be ignored as they are considered transient states.
+ */
+ private static final long MIN_REGISTRATION_DURATION_MILLIS = 1L * SECOND_IN_MILLIS;
+
+ /**
+ * Maximum length of the extra message in the termination reason.
+ *
+ * <p>If the extra message is longer than this length, it will be truncated.
+ */
+ private static final int MAX_EXTRA_MESSAGE_LENGTH = 128;
+
+ private final ImsPhone mPhone;
+ private final PersistAtomsStorage mStorage;
+
+ @ImsRegistrationState private int mLastRegistrationState = REGISTRATION_STATE_NOT_REGISTERED;
+
+ private long mLastTimestamp;
+ @Nullable private ImsRegistrationStats mLastRegistrationStats;
+
+ // Available features are those reported by ImsService to be available for use.
+ private MmTelCapabilities mLastAvailableFeatures = new MmTelCapabilities();
+
+ // Capable features (enabled by device/carrier). Theses are available before IMS is registered
+ // and not necessarily updated when RAT changes.
+ private final MmTelCapabilities mLastWwanCapableFeatures = new MmTelCapabilities();
+ private final MmTelCapabilities mLastWlanCapableFeatures = new MmTelCapabilities();
+
+ public ImsStats(ImsPhone phone) {
+ mPhone = phone;
+ mStorage = PhoneFactory.getMetricsCollector().getAtomsStorage();
+ }
+
+ /**
+ * Finalizes the durations of the current IMS registration stats segment.
+ *
+ * <p>This method is also invoked whenever the registration state, feature capability, or
+ * feature availability changes.
+ */
+ public synchronized void conclude() {
+ long now = getTimeMillis();
+
+ // Currently not tracking time spent on registering.
+ if (mLastRegistrationState == REGISTRATION_STATE_REGISTERED) {
+ ImsRegistrationStats stats = copyOf(mLastRegistrationStats);
+ long duration = now - mLastTimestamp;
+
+ if (duration < MIN_REGISTRATION_DURATION_MILLIS) {
+ logw("conclude: discarding transient stats, duration=%d", duration);
+ } else {
+ stats.registeredMillis = duration;
+
+ stats.voiceAvailableMillis =
+ mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_VOICE) ? duration : 0;
+ stats.videoAvailableMillis =
+ mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_VIDEO) ? duration : 0;
+ stats.utAvailableMillis =
+ mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_UT) ? duration : 0;
+ stats.smsAvailableMillis =
+ mLastAvailableFeatures.isCapable(CAPABILITY_TYPE_SMS) ? duration : 0;
+
+ MmTelCapabilities lastCapableFeatures =
+ stats.rat == TelephonyManager.NETWORK_TYPE_IWLAN
+ ? mLastWlanCapableFeatures
+ : mLastWwanCapableFeatures;
+ stats.voiceCapableMillis =
+ lastCapableFeatures.isCapable(CAPABILITY_TYPE_VOICE) ? duration : 0;
+ stats.videoCapableMillis =
+ lastCapableFeatures.isCapable(CAPABILITY_TYPE_VIDEO) ? duration : 0;
+ stats.utCapableMillis =
+ lastCapableFeatures.isCapable(CAPABILITY_TYPE_UT) ? duration : 0;
+ stats.smsCapableMillis =
+ lastCapableFeatures.isCapable(CAPABILITY_TYPE_SMS) ? duration : 0;
+
+ mStorage.addImsRegistrationStats(stats);
+ }
+ }
+
+ mLastTimestamp = now;
+ }
+
+ /** Updates the stats when registered features changed. */
+ public synchronized void onImsCapabilitiesChanged(
+ @ImsRegistrationTech int radioTech, MmTelCapabilities capabilities) {
+ conclude();
+
+ if (mLastRegistrationStats != null) {
+ mLastRegistrationStats.rat = convertRegistrationTechToNetworkType(radioTech);
+ }
+ mLastAvailableFeatures = capabilities;
+ }
+
+ /** Updates the stats when capable features changed. */
+ public synchronized void onSetFeatureResponse(
+ @MmTelCapability int feature, @ImsRegistrationTech int network, int value) {
+ MmTelCapabilities lastCapableFeatures = getLastCapableFeaturesForTech(network);
+ if (lastCapableFeatures != null) {
+ conclude();
+ if (value == ProvisioningManager.PROVISIONING_VALUE_ENABLED) {
+ lastCapableFeatures.addCapabilities(feature);
+ } else {
+ lastCapableFeatures.removeCapabilities(feature);
+ }
+ }
+ }
+
+ /** Updates the stats when IMS registration is progressing. */
+ public synchronized void onImsRegistering(@TransportType int imsRadioTech) {
+ conclude();
+
+ mLastRegistrationStats = getDefaultImsRegistrationStats();
+ mLastRegistrationStats.rat = convertTransportTypeToNetworkType(imsRadioTech);
+ mLastRegistrationState = REGISTRATION_STATE_REGISTERING;
+ }
+
+ /** Updates the stats when IMS registration succeeds. */
+ public synchronized void onImsRegistered(@TransportType int imsRadioTech) {
+ conclude();
+
+ // NOTE: mLastRegistrationStats can be null (no registering phase).
+ if (mLastRegistrationStats == null) {
+ mLastRegistrationStats = getDefaultImsRegistrationStats();
+ }
+ mLastRegistrationStats.rat = convertTransportTypeToNetworkType(imsRadioTech);
+ mLastRegistrationState = REGISTRATION_STATE_REGISTERED;
+ }
+
+ /** Updates the stats and generates a termination atom when IMS registration fails/ends. */
+ public synchronized void onImsUnregistered(ImsReasonInfo reasonInfo) {
+ conclude();
+
+ // Generate end reason atom.
+ // NOTE: mLastRegistrationStats can be null (no registering phase).
+ ImsRegistrationTermination termination = new ImsRegistrationTermination();
+ if (mLastRegistrationStats != null) {
+ termination.carrierId = mLastRegistrationStats.carrierId;
+ termination.ratAtEnd = getRatAtEnd(mLastRegistrationStats.rat);
+ } else {
+ termination.carrierId = mPhone.getDefaultPhone().getCarrierId();
+ // We cannot tell whether the registration was intended for WWAN or WLAN
+ termination.ratAtEnd = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+ termination.isMultiSim = SimSlotState.isMultiSim();
+ termination.setupFailed = (mLastRegistrationState != REGISTRATION_STATE_REGISTERED);
+ termination.reasonCode = reasonInfo.getCode();
+ termination.extraCode = reasonInfo.getExtraCode();
+ termination.extraMessage = sanitizeExtraMessage(reasonInfo.getExtraMessage());
+ termination.count = 1;
+ mStorage.addImsRegistrationTermination(termination);
+
+ // Reset state to unregistered.
+ mLastRegistrationState = REGISTRATION_STATE_NOT_REGISTERED;
+ mLastRegistrationStats = null;
+ mLastAvailableFeatures = new MmTelCapabilities();
+ }
+
+ @NetworkType
+ private int getRatAtEnd(@NetworkType int lastStateRat) {
+ return lastStateRat == TelephonyManager.NETWORK_TYPE_IWLAN ? lastStateRat : getWwanPsRat();
+ }
+
+ @NetworkType
+ private int convertTransportTypeToNetworkType(@TransportType int transportType) {
+ switch (transportType) {
+ case AccessNetworkConstants.TRANSPORT_TYPE_WWAN:
+ return getWwanPsRat();
+ case AccessNetworkConstants.TRANSPORT_TYPE_WLAN:
+ return TelephonyManager.NETWORK_TYPE_IWLAN;
+ default:
+ return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+ }
+
+ @NetworkType
+ private int getWwanPsRat() {
+ ServiceState state = mPhone.getServiceStateTracker().getServiceState();
+ final NetworkRegistrationInfo wwanRegInfo =
+ state.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ return wwanRegInfo != null
+ ? wwanRegInfo.getAccessNetworkTechnology()
+ : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+
+ private ImsRegistrationStats getDefaultImsRegistrationStats() {
+ Phone phone = mPhone.getDefaultPhone();
+ ImsRegistrationStats stats = new ImsRegistrationStats();
+ stats.carrierId = phone.getCarrierId();
+ stats.simSlotIndex = phone.getPhoneId();
+ return stats;
+ }
+
+ @Nullable
+ private MmTelCapabilities getLastCapableFeaturesForTech(@ImsRegistrationTech int radioTech) {
+ switch (radioTech) {
+ case REGISTRATION_TECH_NONE:
+ return null;
+ case REGISTRATION_TECH_IWLAN:
+ return mLastWlanCapableFeatures;
+ default:
+ return mLastWwanCapableFeatures;
+ }
+ }
+
+ @NetworkType
+ private int convertRegistrationTechToNetworkType(@ImsRegistrationTech int radioTech) {
+ switch (radioTech) {
+ case REGISTRATION_TECH_NONE:
+ return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ case REGISTRATION_TECH_LTE:
+ return TelephonyManager.NETWORK_TYPE_LTE;
+ case REGISTRATION_TECH_IWLAN:
+ return TelephonyManager.NETWORK_TYPE_IWLAN;
+ default:
+ // TODO: for VoNR, need to add registration tech to ImsRegistrationImplBase
+ loge("convertRegistrationTechToNetworkType: unknown radio tech %d", radioTech);
+ return getWwanPsRat();
+ }
+ }
+
+ private static ImsRegistrationStats copyOf(ImsRegistrationStats source) {
+ ImsRegistrationStats dest = new ImsRegistrationStats();
+
+ dest.carrierId = source.carrierId;
+ dest.simSlotIndex = source.simSlotIndex;
+ dest.rat = source.rat;
+ dest.registeredMillis = source.registeredMillis;
+ dest.voiceCapableMillis = source.voiceCapableMillis;
+ dest.voiceAvailableMillis = source.voiceAvailableMillis;
+ dest.smsCapableMillis = source.smsCapableMillis;
+ dest.smsAvailableMillis = source.smsAvailableMillis;
+ dest.videoCapableMillis = source.videoCapableMillis;
+ dest.videoAvailableMillis = source.videoAvailableMillis;
+ dest.utCapableMillis = source.utCapableMillis;
+ dest.utAvailableMillis = source.utAvailableMillis;
+
+ return dest;
+ }
+
+ @VisibleForTesting
+ protected long getTimeMillis() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ private static String sanitizeExtraMessage(@Nullable String str) {
+ if (str == null) {
+ return "";
+ }
+ return str.length() > MAX_EXTRA_MESSAGE_LENGTH
+ ? str.substring(0, MAX_EXTRA_MESSAGE_LENGTH)
+ : str;
+ }
+
+ private void logw(String format, Object... args) {
+ Rlog.w(TAG, "[" + mPhone.getPhoneId() + "] " + String.format(format, args));
+ }
+
+ private void loge(String format, Object... args) {
+ Rlog.e(TAG, "[" + mPhone.getPhoneId() + "] " + String.format(format, args));
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
index af71c54..8b2796e 100644
--- a/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+++ b/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
@@ -20,6 +20,14 @@
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
+import static com.android.internal.telephony.TelephonyStatsLog.CARRIER_ID_TABLE_VERSION;
+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.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.SIM_SLOT_STATE;
import static com.android.internal.telephony.TelephonyStatsLog.SUPPORTED_RADIO_ACCESS_FAMILY;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE;
@@ -33,7 +41,15 @@
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.RawVoiceCallRatUsage;
+import com.android.internal.telephony.imsphone.ImsPhone;
+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.ImsRegistrationStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
+import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
+import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
import com.android.internal.util.ConcurrentUtils;
import com.android.telephony.Rlog;
@@ -82,20 +98,32 @@
private PersistAtomsStorage mStorage;
private final StatsManager mStatsManager;
+ private final AirplaneModeStats mAirplaneModeStats;
private static final Random sRandom = new Random();
public MetricsCollector(Context context) {
mStorage = new PersistAtomsStorage(context);
mStatsManager = (StatsManager) context.getSystemService(Context.STATS_MANAGER);
if (mStatsManager != null) {
+ registerAtom(CELLULAR_DATA_SERVICE_SWITCH, POLICY_PULL_DAILY);
+ registerAtom(CELLULAR_SERVICE_STATE, POLICY_PULL_DAILY);
registerAtom(SIM_SLOT_STATE, null);
registerAtom(SUPPORTED_RADIO_ACCESS_FAMILY, null);
registerAtom(VOICE_CALL_RAT_USAGE, POLICY_PULL_DAILY);
registerAtom(VOICE_CALL_SESSION, POLICY_PULL_DAILY);
+ registerAtom(INCOMING_SMS, POLICY_PULL_DAILY);
+ registerAtom(OUTGOING_SMS, POLICY_PULL_DAILY);
+ registerAtom(CARRIER_ID_TABLE_VERSION, null);
+ registerAtom(DATA_CALL_SESSION, POLICY_PULL_DAILY);
+ registerAtom(IMS_REGISTRATION_STATS, POLICY_PULL_DAILY);
+ registerAtom(IMS_REGISTRATION_TERMINATION, POLICY_PULL_DAILY);
+
Rlog.d(TAG, "registered");
} else {
Rlog.e(TAG, "could not get StatsManager, atoms not registered");
}
+
+ mAirplaneModeStats = new AirplaneModeStats(context);
}
/** Replaces the {@link PersistAtomsStorage} backing the puller. Used during unit tests. */
@@ -114,6 +142,10 @@
@Override
public int onPullAtom(int atomTag, List<StatsEvent> data) {
switch (atomTag) {
+ case CELLULAR_DATA_SERVICE_SWITCH:
+ return pullCellularDataServiceSwitch(data);
+ case CELLULAR_SERVICE_STATE:
+ return pullCellularServiceState(data);
case SIM_SLOT_STATE:
return pullSimSlotState(data);
case SUPPORTED_RADIO_ACCESS_FAMILY:
@@ -122,6 +154,18 @@
return pullVoiceCallRatUsages(data);
case VOICE_CALL_SESSION:
return pullVoiceCallSessions(data);
+ case INCOMING_SMS:
+ return pullIncomingSms(data);
+ case OUTGOING_SMS:
+ return pullOutgoingSms(data);
+ case CARRIER_ID_TABLE_VERSION:
+ return pullCarrierIdTableVersion(data);
+ case DATA_CALL_SESSION:
+ return pullDataCallSession(data);
+ case IMS_REGISTRATION_STATS:
+ return pullImsRegistrationStats(data);
+ case IMS_REGISTRATION_TERMINATION:
+ return pullImsRegistrationTermination(data);
default:
Rlog.e(TAG, String.format("unexpected atom ID %d", atomTag));
return StatsManager.PULL_SKIP;
@@ -154,17 +198,17 @@
}
private static int pullSupportedRadioAccessFamily(List<StatsEvent> data) {
- long rafSupported = 0L;
- try {
- // The bitmask is defined in android.telephony.TelephonyManager.NetworkTypeBitMask
- for (Phone phone : PhoneFactory.getPhones()) {
- rafSupported |= phone.getRadioAccessFamily();
- }
- } catch (IllegalStateException e) {
- // Phones have not been made yet
+ Phone[] phones = getPhonesIfAny();
+ if (phones.length == 0) {
return StatsManager.PULL_SKIP;
}
+ // The bitmask is defined in android.telephony.TelephonyManager.NetworkTypeBitMask
+ long rafSupported = 0L;
+ for (Phone phone : PhoneFactory.getPhones()) {
+ rafSupported |= phone.getRadioAccessFamily();
+ }
+
StatsEvent e =
StatsEvent.newBuilder()
.setAtomId(SUPPORTED_RADIO_ACCESS_FAMILY)
@@ -174,8 +218,25 @@
return StatsManager.PULL_SUCCESS;
}
+ private static int pullCarrierIdTableVersion(List<StatsEvent> data) {
+ Phone[] phones = getPhonesIfAny();
+ if (phones.length == 0) {
+ return StatsManager.PULL_SKIP;
+ } else {
+ // All phones should have the same version of the carrier ID table, so only query the
+ // first one.
+ int version = phones[0].getCarrierIdListVersion();
+ data.add(
+ StatsEvent.newBuilder()
+ .setAtomId(CARRIER_ID_TABLE_VERSION)
+ .writeInt(version)
+ .build());
+ return StatsManager.PULL_SUCCESS;
+ }
+ }
+
private int pullVoiceCallRatUsages(List<StatsEvent> data) {
- RawVoiceCallRatUsage[] usages = mStorage.getVoiceCallRatUsages(MIN_COOLDOWN_MILLIS);
+ VoiceCallRatUsage[] usages = mStorage.getVoiceCallRatUsages(MIN_COOLDOWN_MILLIS);
if (usages != null) {
// sort by carrier/RAT and remove buckets with insufficient number of calls
Arrays.stream(usages)
@@ -199,7 +260,7 @@
private int pullVoiceCallSessions(List<StatsEvent> data) {
VoiceCallSession[] calls = mStorage.getVoiceCallSessions(MIN_COOLDOWN_MILLIS);
if (calls != null) {
- // call session list is already shuffled when calls inserted
+ // call session list is already shuffled when calls were inserted
Arrays.stream(calls).forEach(call -> data.add(buildStatsEvent(call)));
return StatsManager.PULL_SUCCESS;
} else {
@@ -208,12 +269,146 @@
}
}
+ private int pullIncomingSms(List<StatsEvent> data) {
+ IncomingSms[] smsList = mStorage.getIncomingSms(MIN_COOLDOWN_MILLIS);
+ if (smsList != null) {
+ // SMS list is already shuffled when SMS were inserted
+ Arrays.stream(smsList).forEach(sms -> data.add(buildStatsEvent(sms)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "INCOMING_SMS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullOutgoingSms(List<StatsEvent> data) {
+ OutgoingSms[] smsList = mStorage.getOutgoingSms(MIN_COOLDOWN_MILLIS);
+ if (smsList != null) {
+ // SMS list is already shuffled when SMS were inserted
+ Arrays.stream(smsList).forEach(sms -> data.add(buildStatsEvent(sms)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "OUTGOING_SMS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullDataCallSession(List<StatsEvent> data) {
+ DataCallSession[] dataCallSessions = mStorage.getDataCallSessions(MIN_COOLDOWN_MILLIS);
+ if (dataCallSessions != null) {
+ Arrays.stream(dataCallSessions)
+ .forEach(dataCall -> data.add(buildStatsEvent(dataCall)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "DATA_CALL_SESSION pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullCellularDataServiceSwitch(List<StatsEvent> data) {
+ CellularDataServiceSwitch[] persistAtoms =
+ mStorage.getCellularDataServiceSwitches(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ // list is already shuffled when instances were inserted
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "CELLULAR_DATA_SERVICE_SWITCH pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullCellularServiceState(List<StatsEvent> data) {
+ // Include the latest durations
+ for (Phone phone : getPhonesIfAny()) {
+ phone.getServiceStateTracker().getServiceStateStats().conclude();
+ }
+
+ CellularServiceState[] persistAtoms =
+ mStorage.getCellularServiceStates(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ // list is already shuffled when instances were inserted
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "CELLULAR_SERVICE_STATE pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullImsRegistrationStats(List<StatsEvent> data) {
+ // Include the latest durations
+ for (Phone phone : getPhonesIfAny()) {
+ ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
+ if (imsPhone != null) {
+ imsPhone.getImsStats().conclude();
+ }
+ }
+
+ ImsRegistrationStats[] persistAtoms = mStorage.getImsRegistrationStats(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ // list is already shuffled when instances were inserted
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "IMS_REGISTRATION_STATS pull too frequent, skipping");
+ return StatsManager.PULL_SKIP;
+ }
+ }
+
+ private int pullImsRegistrationTermination(List<StatsEvent> data) {
+ ImsRegistrationTermination[] persistAtoms =
+ mStorage.getImsRegistrationTerminations(MIN_COOLDOWN_MILLIS);
+ if (persistAtoms != null) {
+ // list is already shuffled when instances were inserted
+ Arrays.stream(persistAtoms)
+ .forEach(persistAtom -> data.add(buildStatsEvent(persistAtom)));
+ return StatsManager.PULL_SUCCESS;
+ } else {
+ Rlog.w(TAG, "IMS_REGISTRATION_TERMINATION 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);
}
- private static StatsEvent buildStatsEvent(RawVoiceCallRatUsage usage) {
+ private static StatsEvent buildStatsEvent(CellularDataServiceSwitch serviceSwitch) {
+ return StatsEvent.newBuilder()
+ .setAtomId(CELLULAR_DATA_SERVICE_SWITCH)
+ .writeInt(serviceSwitch.ratFrom)
+ .writeInt(serviceSwitch.ratTo)
+ .writeInt(serviceSwitch.simSlotIndex)
+ .writeBoolean(serviceSwitch.isMultiSim)
+ .writeInt(serviceSwitch.carrierId)
+ .writeInt(serviceSwitch.switchCount)
+ .build();
+ }
+
+ private static StatsEvent buildStatsEvent(CellularServiceState state) {
+ return StatsEvent.newBuilder()
+ .setAtomId(CELLULAR_SERVICE_STATE)
+ .writeInt(state.voiceRat)
+ .writeInt(state.dataRat)
+ .writeInt(state.voiceRoamingType)
+ .writeInt(state.dataRoamingType)
+ .writeBoolean(state.isEndc)
+ .writeInt(state.simSlotIndex)
+ .writeBoolean(state.isMultiSim)
+ .writeInt(state.carrierId)
+ .writeInt(
+ (int)
+ (round(state.totalTimeMillis, DURATION_BUCKET_MILLIS)
+ / SECOND_IN_MILLIS))
+ .build();
+ }
+
+ private static StatsEvent buildStatsEvent(VoiceCallRatUsage usage) {
return StatsEvent.newBuilder()
.setAtomId(VOICE_CALL_RAT_USAGE)
.writeInt(usage.carrierId)
@@ -253,9 +448,149 @@
.writeBoolean(session.isRoaming)
// workaround: dimension required for keeping multiple pulled atoms
.writeInt(sRandom.nextInt())
+ // New fields introduced in Android S
+ .writeInt(session.signalStrengthAtEnd)
+ .writeInt(session.bandAtEnd)
+ .writeInt(session.setupDurationMillis)
+ .writeInt(session.mainCodecQuality)
+ .writeBoolean(session.videoEnabled)
+ .writeInt(session.ratAtConnected)
+ .writeBoolean(session.isMultiparty)
.build();
}
+ private static StatsEvent buildStatsEvent(IncomingSms sms) {
+ return StatsEvent.newBuilder()
+ .setAtomId(INCOMING_SMS)
+ .writeInt(sms.smsFormat)
+ .writeInt(sms.smsTech)
+ .writeInt(sms.rat)
+ .writeInt(sms.smsType)
+ .writeInt(sms.totalParts)
+ .writeInt(sms.receivedParts)
+ .writeBoolean(sms.blocked)
+ .writeInt(sms.error)
+ .writeBoolean(sms.isRoaming)
+ .writeInt(sms.simSlotIndex)
+ .writeBoolean(sms.isMultiSim)
+ .writeBoolean(sms.isEsim)
+ .writeInt(sms.carrierId)
+ .writeLong(sms.messageId)
+ .build();
+ }
+
+ private static StatsEvent buildStatsEvent(OutgoingSms sms) {
+ return StatsEvent.newBuilder()
+ .setAtomId(OUTGOING_SMS)
+ .writeInt(sms.smsFormat)
+ .writeInt(sms.smsTech)
+ .writeInt(sms.rat)
+ .writeInt(sms.sendResult)
+ .writeInt(sms.errorCode)
+ .writeBoolean(sms.isRoaming)
+ .writeBoolean(sms.isFromDefaultApp)
+ .writeInt(sms.simSlotIndex)
+ .writeBoolean(sms.isMultiSim)
+ .writeBoolean(sms.isEsim)
+ .writeInt(sms.carrierId)
+ .writeLong(sms.messageId)
+ .writeInt(sms.retryId)
+ .build();
+ }
+
+ private static StatsEvent buildStatsEvent(DataCallSession dataCallSession) {
+ return StatsEvent.newBuilder()
+ .setAtomId(DATA_CALL_SESSION)
+ .writeInt(dataCallSession.dimension)
+ .writeBoolean(dataCallSession.isMultiSim)
+ .writeBoolean(dataCallSession.isEsim)
+ .writeInt(0) // profile is deprecated, so we default to 0
+ .writeInt(dataCallSession.apnTypeBitmask)
+ .writeInt(dataCallSession.carrierId)
+ .writeBoolean(dataCallSession.isRoaming)
+ .writeInt(dataCallSession.ratAtEnd)
+ .writeBoolean(dataCallSession.oosAtEnd)
+ .writeLong(dataCallSession.ratSwitchCount)
+ .writeBoolean(dataCallSession.isOpportunistic)
+ .writeInt(dataCallSession.ipType)
+ .writeBoolean(dataCallSession.setupFailed)
+ .writeInt(dataCallSession.failureCause)
+ .writeInt(dataCallSession.suggestedRetryMillis)
+ .writeInt(dataCallSession.deactivateReason)
+ .writeLong(dataCallSession.durationMinutes)
+ .writeBoolean(dataCallSession.ongoing)
+ .build();
+ }
+
+ private static StatsEvent buildStatsEvent(ImsRegistrationStats stats) {
+ return StatsEvent.newBuilder()
+ .setAtomId(IMS_REGISTRATION_STATS)
+ .writeInt(stats.carrierId)
+ .writeInt(stats.simSlotIndex)
+ .writeInt(stats.rat)
+ .writeInt(
+ (int)
+ (round(stats.registeredMillis, DURATION_BUCKET_MILLIS)
+ / SECOND_IN_MILLIS))
+ .writeInt(
+ (int)
+ (round(stats.voiceCapableMillis, DURATION_BUCKET_MILLIS)
+ / SECOND_IN_MILLIS))
+ .writeInt(
+ (int)
+ (round(stats.voiceAvailableMillis, DURATION_BUCKET_MILLIS)
+ / SECOND_IN_MILLIS))
+ .writeInt(
+ (int)
+ (round(stats.smsCapableMillis, DURATION_BUCKET_MILLIS)
+ / SECOND_IN_MILLIS))
+ .writeInt(
+ (int)
+ (round(stats.smsAvailableMillis, DURATION_BUCKET_MILLIS)
+ / SECOND_IN_MILLIS))
+ .writeInt(
+ (int)
+ (round(stats.videoCapableMillis, DURATION_BUCKET_MILLIS)
+ / SECOND_IN_MILLIS))
+ .writeInt(
+ (int)
+ (round(stats.videoAvailableMillis, DURATION_BUCKET_MILLIS)
+ / SECOND_IN_MILLIS))
+ .writeInt(
+ (int)
+ (round(stats.utCapableMillis, DURATION_BUCKET_MILLIS)
+ / SECOND_IN_MILLIS))
+ .writeInt(
+ (int)
+ (round(stats.utAvailableMillis, DURATION_BUCKET_MILLIS)
+ / SECOND_IN_MILLIS))
+ .build();
+ }
+
+ private static StatsEvent buildStatsEvent(ImsRegistrationTermination termination) {
+ return StatsEvent.newBuilder()
+ .setAtomId(IMS_REGISTRATION_TERMINATION)
+ .writeInt(termination.carrierId)
+ .writeBoolean(termination.isMultiSim)
+ .writeInt(termination.ratAtEnd)
+ .writeBoolean(termination.setupFailed)
+ .writeInt(termination.reasonCode)
+ .writeInt(termination.extraCode)
+ .writeString(termination.extraMessage)
+ .writeInt(termination.count)
+ .build();
+ }
+
+ /** Returns all phones in {@link PhoneFactory}, or an empty array if phones not made yet. */
+ private static Phone[] getPhonesIfAny() {
+ try {
+ return PhoneFactory.getPhones();
+ } catch (IllegalStateException e) {
+ // Phones have not been made yet
+ return new Phone[0];
+ }
+ }
+
/** Returns the value rounded to the bucket. */
private static long round(long value, long bucket) {
return ((value + bucket / 2) / bucket) * bucket;
diff --git a/src/java/com/android/internal/telephony/metrics/ModemRestartStats.java b/src/java/com/android/internal/telephony/metrics/ModemRestartStats.java
new file mode 100644
index 0000000..343bd42
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/ModemRestartStats.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 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.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import static com.android.internal.telephony.TelephonyStatsLog.MODEM_RESTART;
+
+import android.os.Build;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.TelephonyStatsLog;
+import com.android.telephony.Rlog;
+
+/** Metrics for the modem restarts. */
+public class ModemRestartStats {
+ private static final String TAG = ModemRestartStats.class.getSimpleName();
+
+ /* Maximum length of the baseband version. */
+ private static final int MAX_BASEBAND_LEN = 100;
+
+ /* Maximum length of the modem restart reason. */
+ private static final int MAX_REASON_LEN = 100;
+
+ private ModemRestartStats() { }
+
+ /** Generate metrics when modem restart occurs. */
+ public static void onModemRestart(String reason) {
+ reason = truncateString(reason, MAX_REASON_LEN);
+ String basebandVersion = truncateString(Build.getRadioVersion(), MAX_BASEBAND_LEN);
+ int carrierId = getCarrierId();
+
+ Rlog.d(TAG, "Modem restart (carrier=" + carrierId + "): " + reason);
+ TelephonyStatsLog.write(MODEM_RESTART, basebandVersion, reason, carrierId);
+ }
+
+ private static String truncateString(String string, int maxLen) {
+ string = nullToEmpty(string);
+ if (string.length() > maxLen) {
+ string = string.substring(0, maxLen);
+ }
+ return string;
+ }
+
+ private static String nullToEmpty(String string) {
+ return string != null ? string : "";
+ }
+
+ /** Returns the carrier ID of the first SIM card for which carrier ID is available. */
+ private static int getCarrierId() {
+ int carrierId = INVALID_SUBSCRIPTION_ID;
+ try {
+ for (Phone phone : PhoneFactory.getPhones()) {
+ carrierId = phone.getCarrierId();
+ if (carrierId != INVALID_SUBSCRIPTION_ID) {
+ break;
+ }
+ }
+ } catch (IllegalStateException e) {
+ // Nothing to do here.
+ }
+ return carrierId;
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
index f208369..3aca215 100644
--- a/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
+++ b/src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java
@@ -18,18 +18,32 @@
import android.annotation.Nullable;
import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.telephony.TelephonyManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.nano.PersistAtomsProto.CarrierIdMismatch;
+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.ImsRegistrationStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
+import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms;
-import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage;
+import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
+import com.android.internal.util.ArrayUtils;
import com.android.telephony.Rlog;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
import java.security.SecureRandom;
import java.util.Arrays;
+import java.util.stream.IntStream;
/**
* Stores and aggregates metrics that should not be pulled at arbitrary frequency.
@@ -43,51 +57,239 @@
/** Name of the file where cached statistics are saved to. */
private static final String FILENAME = "persist_atoms.pb";
- /** Maximum number of call sessions to store during pulls. */
+ /** Delay to store atoms to persistent storage to bundle multiple operations together. */
+ private static final int SAVE_TO_FILE_DELAY_MILLIS = 30000;
+
+ /** Maximum number of call sessions to store between pulls. */
private static final int MAX_NUM_CALL_SESSIONS = 50;
+ /**
+ * Maximum number of SMS to store between pulls. Incoming messages and outgoing messages are
+ * counted separately.
+ */
+ private static final int MAX_NUM_SMS = 25;
+
+ /**
+ * Maximum number of carrier ID mismatch events stored on the device to avoid sending duplicated
+ * metrics.
+ */
+ private static final int MAX_CARRIER_ID_MISMATCH = 40;
+
+ /** Maximum number of data call sessions to store during pulls. */
+ private static final int MAX_NUM_DATA_CALL_SESSIONS = 15;
+
+ /** Maximum number of service states to store between pulls. */
+ private static final int MAX_NUM_CELLULAR_SERVICE_STATES = 50;
+
+ /** Maximum number of data service switches to store between pulls. */
+ private static final int MAX_NUM_CELLULAR_DATA_SERVICE_SWITCHES = 50;
+
+ /** Maximum number of IMS registration stats to store between pulls. */
+ private static final int MAX_NUM_IMS_REGISTRATION_STATS = 10;
+
+ /** Maximum number of IMS registration terminations to store between pulls. */
+ private static final int MAX_NUM_IMS_REGISTRATION_TERMINATIONS = 10;
+
/** Stores persist atoms and persist states of the puller. */
@VisibleForTesting protected final PersistAtoms mAtoms;
/** Aggregates RAT duration and call count. */
private final VoiceCallRatTracker mVoiceCallRatTracker;
+ /** Delay before data is stored persistenly to storage. */
+ @VisibleForTesting protected int mSaveDelay;
+
private final Context mContext;
+ private final Handler mHandler;
+ private final HandlerThread mHandlerThread;
private static final SecureRandom sRandom = new SecureRandom();
+ private Runnable mSaveRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ saveAtomsToFileNow();
+ }
+ };
+
public PersistAtomsStorage(Context context) {
mContext = context;
mAtoms = loadAtomsFromFile();
- mVoiceCallRatTracker = VoiceCallRatTracker.fromProto(mAtoms.rawVoiceCallRatUsage);
+ mVoiceCallRatTracker = VoiceCallRatTracker.fromProto(mAtoms.voiceCallRatUsage);
+
+ mHandlerThread = new HandlerThread("PersistAtomsThread");
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mSaveDelay = SAVE_TO_FILE_DELAY_MILLIS;
}
/** Adds a call to the storage. */
public synchronized void addVoiceCallSession(VoiceCallSession call) {
- int newLength = mAtoms.voiceCallSession.length + 1;
- if (newLength > MAX_NUM_CALL_SESSIONS) {
- // will evict one previous call randomly instead of making the array larger
- newLength = MAX_NUM_CALL_SESSIONS;
- } else {
- mAtoms.voiceCallSession = Arrays.copyOf(mAtoms.voiceCallSession, newLength);
- }
- int insertAt = 0;
- if (newLength > 1) {
- // shuffle when each call is added, or randomly replace a previous call instead if
- // MAX_NUM_CALL_SESSIONS is reached (call at the last index is evicted).
- insertAt = sRandom.nextInt(newLength);
- mAtoms.voiceCallSession[newLength - 1] = mAtoms.voiceCallSession[insertAt];
- }
- mAtoms.voiceCallSession[insertAt] = call;
+ mAtoms.voiceCallSession =
+ insertAtRandomPlace(mAtoms.voiceCallSession, call, MAX_NUM_CALL_SESSIONS);
saveAtomsToFile();
+
+ Rlog.d(TAG, "Add new voice call session: " + call.toString());
}
/** Adds RAT usages to the storage when a call session ends. */
public synchronized void addVoiceCallRatUsage(VoiceCallRatTracker ratUsages) {
mVoiceCallRatTracker.mergeWith(ratUsages);
- mAtoms.rawVoiceCallRatUsage = mVoiceCallRatTracker.toProto();
+ mAtoms.voiceCallRatUsage = mVoiceCallRatTracker.toProto();
saveAtomsToFile();
}
+ /** Adds an incoming SMS to the storage. */
+ public synchronized void addIncomingSms(IncomingSms sms) {
+ mAtoms.incomingSms = insertAtRandomPlace(mAtoms.incomingSms, sms, MAX_NUM_SMS);
+ saveAtomsToFile();
+
+ // To be removed
+ Rlog.d(TAG, "Add new incoming SMS atom: " + sms.toString());
+ }
+
+ /** Adds an outgoing SMS to the storage. */
+ public synchronized void addOutgoingSms(OutgoingSms sms) {
+ // Update the retry id, if needed, so that it's unique and larger than all
+ // previous ones. (this algorithm ignores the fact that some SMS atoms might
+ // be dropped due to limit in size of the array).
+ for (OutgoingSms storedSms : mAtoms.outgoingSms) {
+ if (storedSms.messageId == sms.messageId && storedSms.retryId >= sms.retryId) {
+ sms.retryId = storedSms.retryId + 1;
+ }
+ }
+
+ mAtoms.outgoingSms = insertAtRandomPlace(mAtoms.outgoingSms, sms, MAX_NUM_SMS);
+ saveAtomsToFile();
+
+ // To be removed
+ Rlog.d(TAG, "Add new outgoing SMS atom: " + sms.toString());
+ }
+
+ /** Adds a service state to the storage, together with data service switch if any. */
+ public synchronized void addCellularServiceStateAndCellularDataServiceSwitch(
+ CellularServiceState state, @Nullable CellularDataServiceSwitch serviceSwitch) {
+ CellularServiceState existingState = find(state);
+ if (existingState != null) {
+ existingState.totalTimeMillis += state.totalTimeMillis;
+ existingState.lastUsedMillis = getWallTimeMillis();
+ } else {
+ state.lastUsedMillis = getWallTimeMillis();
+ mAtoms.cellularServiceState =
+ insertAtRandomPlace(
+ mAtoms.cellularServiceState, state, MAX_NUM_CELLULAR_SERVICE_STATES);
+ }
+
+ if (serviceSwitch != null) {
+ CellularDataServiceSwitch existingSwitch = find(serviceSwitch);
+ if (existingSwitch != null) {
+ existingSwitch.switchCount += serviceSwitch.switchCount;
+ existingSwitch.lastUsedMillis = getWallTimeMillis();
+ } else {
+ serviceSwitch.lastUsedMillis = getWallTimeMillis();
+ mAtoms.cellularDataServiceSwitch =
+ insertAtRandomPlace(
+ mAtoms.cellularDataServiceSwitch,
+ serviceSwitch,
+ MAX_NUM_CELLULAR_DATA_SERVICE_SWITCHES);
+ }
+ }
+
+ saveAtomsToFile();
+ }
+
+ /** Adds a data call session to the storage. */
+ public synchronized void addDataCallSession(DataCallSession dataCall) {
+ mAtoms.dataCallSession =
+ insertAtRandomPlace(mAtoms.dataCallSession, dataCall, MAX_NUM_DATA_CALL_SESSIONS);
+ saveAtomsToFile();
+ }
+
+ /**
+ * Adds a new carrier ID mismatch event to the storage.
+ *
+ * @return true if the item was not present and was added to the persistent storage, false
+ * otherwise.
+ */
+ public synchronized boolean addCarrierIdMismatch(CarrierIdMismatch carrierIdMismatch) {
+ // Check if the details of the SIM cards are already present and in case return.
+ if (find(carrierIdMismatch) != null) {
+ return false;
+ }
+ // Add the new CarrierIdMismatch at the end of the array, so that the same atom will not be
+ // sent again in future.
+ if (mAtoms.carrierIdMismatch.length == MAX_CARRIER_ID_MISMATCH) {
+ System.arraycopy(
+ mAtoms.carrierIdMismatch,
+ 1,
+ mAtoms.carrierIdMismatch,
+ 0,
+ MAX_CARRIER_ID_MISMATCH - 1);
+ mAtoms.carrierIdMismatch[MAX_CARRIER_ID_MISMATCH - 1] = carrierIdMismatch;
+ } else {
+ int newLength = mAtoms.carrierIdMismatch.length + 1;
+ mAtoms.carrierIdMismatch = Arrays.copyOf(mAtoms.carrierIdMismatch, newLength);
+ mAtoms.carrierIdMismatch[newLength - 1] = carrierIdMismatch;
+ }
+ saveAtomsToFile();
+ return true;
+ }
+
+ /** Adds IMS registration stats to the storage. */
+ public synchronized void addImsRegistrationStats(ImsRegistrationStats stats) {
+ ImsRegistrationStats existingStats = find(stats);
+ if (existingStats != null) {
+ existingStats.registeredMillis += stats.registeredMillis;
+ existingStats.voiceCapableMillis += stats.voiceCapableMillis;
+ existingStats.voiceAvailableMillis += stats.voiceAvailableMillis;
+ existingStats.smsCapableMillis += stats.smsCapableMillis;
+ existingStats.smsAvailableMillis += stats.smsAvailableMillis;
+ existingStats.videoCapableMillis += stats.videoCapableMillis;
+ existingStats.videoAvailableMillis += stats.videoAvailableMillis;
+ existingStats.utCapableMillis += stats.utCapableMillis;
+ existingStats.utAvailableMillis += stats.utAvailableMillis;
+ existingStats.lastUsedMillis = getWallTimeMillis();
+ } else {
+ stats.lastUsedMillis = getWallTimeMillis();
+ mAtoms.imsRegistrationStats =
+ insertAtRandomPlace(
+ mAtoms.imsRegistrationStats, stats, MAX_NUM_IMS_REGISTRATION_STATS);
+ }
+ saveAtomsToFile();
+ }
+
+ /** Adds IMS registration termination to the storage. */
+ public synchronized void addImsRegistrationTermination(ImsRegistrationTermination termination) {
+ ImsRegistrationTermination existingTermination = find(termination);
+ if (existingTermination != null) {
+ existingTermination.count += termination.count;
+ existingTermination.lastUsedMillis = getWallTimeMillis();
+ } else {
+ termination.lastUsedMillis = getWallTimeMillis();
+ mAtoms.imsRegistrationTermination =
+ insertAtRandomPlace(
+ mAtoms.imsRegistrationTermination,
+ termination,
+ MAX_NUM_IMS_REGISTRATION_TERMINATIONS);
+ }
+ saveAtomsToFile();
+ }
+
+ /**
+ * Stores the version of the carrier ID matching table.
+ *
+ * @return true if the version is newer than last available version, false otherwise.
+ */
+ public synchronized boolean setCarrierIdTableVersion(int carrierIdTableVersion) {
+ if (mAtoms.carrierIdTableVersion < carrierIdTableVersion) {
+ mAtoms.carrierIdTableVersion = carrierIdTableVersion;
+ saveAtomsToFile();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
/**
* Returns and clears the voice call sessions if last pulled longer than {@code
* minIntervalMillis} ago, otherwise returns {@code null}.
@@ -110,13 +312,12 @@
* minIntervalMillis} ago, otherwise returns {@code null}.
*/
@Nullable
- public synchronized RawVoiceCallRatUsage[] getVoiceCallRatUsages(long minIntervalMillis) {
- if (getWallTimeMillis() - mAtoms.rawVoiceCallRatUsagePullTimestampMillis
- > minIntervalMillis) {
- mAtoms.rawVoiceCallRatUsagePullTimestampMillis = getWallTimeMillis();
- RawVoiceCallRatUsage[] previousUsages = mAtoms.rawVoiceCallRatUsage;
+ public synchronized VoiceCallRatUsage[] getVoiceCallRatUsages(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.voiceCallRatUsagePullTimestampMillis > minIntervalMillis) {
+ mAtoms.voiceCallRatUsagePullTimestampMillis = getWallTimeMillis();
+ VoiceCallRatUsage[] previousUsages = mAtoms.voiceCallRatUsage;
mVoiceCallRatTracker.clear();
- mAtoms.rawVoiceCallRatUsage = new RawVoiceCallRatUsage[0];
+ mAtoms.voiceCallRatUsage = new VoiceCallRatUsage[0];
saveAtomsToFile();
return previousUsages;
} else {
@@ -124,39 +325,228 @@
}
}
+ /**
+ * Returns and clears the incoming SMS if last pulled longer than {@code minIntervalMillis} ago,
+ * otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized IncomingSms[] getIncomingSms(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.incomingSmsPullTimestampMillis > minIntervalMillis) {
+ mAtoms.incomingSmsPullTimestampMillis = getWallTimeMillis();
+ IncomingSms[] previousIncomingSms = mAtoms.incomingSms;
+ mAtoms.incomingSms = new IncomingSms[0];
+ saveAtomsToFile();
+ return previousIncomingSms;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the outgoing SMS if last pulled longer than {@code minIntervalMillis} ago,
+ * otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized OutgoingSms[] getOutgoingSms(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.outgoingSmsPullTimestampMillis > minIntervalMillis) {
+ mAtoms.outgoingSmsPullTimestampMillis = getWallTimeMillis();
+ OutgoingSms[] previousOutgoingSms = mAtoms.outgoingSms;
+ mAtoms.outgoingSms = new OutgoingSms[0];
+ saveAtomsToFile();
+ return previousOutgoingSms;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the data call session if last pulled longer than {@code minIntervalMillis}
+ * ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized DataCallSession[] getDataCallSessions(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.dataCallSessionPullTimestampMillis > minIntervalMillis) {
+ mAtoms.dataCallSessionPullTimestampMillis = getWallTimeMillis();
+ DataCallSession[] previousDataCallSession = mAtoms.dataCallSession;
+ mAtoms.dataCallSession = new DataCallSession[0];
+ saveAtomsToFile();
+ return previousDataCallSession;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the service state durations if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized CellularServiceState[] getCellularServiceStates(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.cellularServiceStatePullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.cellularServiceStatePullTimestampMillis = getWallTimeMillis();
+ CellularServiceState[] previousStates = mAtoms.cellularServiceState;
+ Arrays.stream(previousStates).forEach(state -> state.lastUsedMillis = 0L);
+ mAtoms.cellularServiceState = new CellularServiceState[0];
+ saveAtomsToFile();
+ return previousStates;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the service state durations if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized CellularDataServiceSwitch[] getCellularDataServiceSwitches(
+ long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.cellularDataServiceSwitchPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.cellularDataServiceSwitchPullTimestampMillis = getWallTimeMillis();
+ CellularDataServiceSwitch[] previousSwitches = mAtoms.cellularDataServiceSwitch;
+ Arrays.stream(previousSwitches)
+ .forEach(serviceSwitch -> serviceSwitch.lastUsedMillis = 0L);
+ mAtoms.cellularDataServiceSwitch = new CellularDataServiceSwitch[0];
+ saveAtomsToFile();
+ return previousSwitches;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the IMS registration statistics if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized ImsRegistrationStats[] getImsRegistrationStats(long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.imsRegistrationStatsPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.imsRegistrationStatsPullTimestampMillis = getWallTimeMillis();
+ ImsRegistrationStats[] previousStats = mAtoms.imsRegistrationStats;
+ Arrays.stream(previousStats).forEach(stats -> stats.lastUsedMillis = 0L);
+ mAtoms.imsRegistrationStats = new ImsRegistrationStats[0];
+ saveAtomsToFile();
+ return previousStats;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns and clears the IMS registration terminations if last pulled longer than {@code
+ * minIntervalMillis} ago, otherwise returns {@code null}.
+ */
+ @Nullable
+ public synchronized ImsRegistrationTermination[] getImsRegistrationTerminations(
+ long minIntervalMillis) {
+ if (getWallTimeMillis() - mAtoms.imsRegistrationTerminationPullTimestampMillis
+ > minIntervalMillis) {
+ mAtoms.imsRegistrationTerminationPullTimestampMillis = getWallTimeMillis();
+ ImsRegistrationTermination[] previousTerminations = mAtoms.imsRegistrationTermination;
+ Arrays.stream(previousTerminations)
+ .forEach(termination -> termination.lastUsedMillis = 0L);
+ mAtoms.imsRegistrationTermination = new ImsRegistrationTermination[0];
+ saveAtomsToFile();
+ return previousTerminations;
+ } else {
+ return null;
+ }
+ }
+
/** Loads {@link PersistAtoms} from a file in private storage. */
private PersistAtoms loadAtomsFromFile() {
try {
- PersistAtoms atomsFromFile =
+ PersistAtoms atoms =
PersistAtoms.parseFrom(
Files.readAllBytes(mContext.getFileStreamPath(FILENAME).toPath()));
// check all the fields in case of situations such as OTA or crash during saving
- if (atomsFromFile.rawVoiceCallRatUsage == null) {
- atomsFromFile.rawVoiceCallRatUsage = new RawVoiceCallRatUsage[0];
- }
- if (atomsFromFile.voiceCallSession == null) {
- atomsFromFile.voiceCallSession = new VoiceCallSession[0];
- }
- if (atomsFromFile.voiceCallSession.length > MAX_NUM_CALL_SESSIONS) {
- atomsFromFile.voiceCallSession =
- Arrays.copyOf(atomsFromFile.voiceCallSession, MAX_NUM_CALL_SESSIONS);
- }
- // out of caution, set timestamps to now if they are missing
- if (atomsFromFile.rawVoiceCallRatUsagePullTimestampMillis == 0L) {
- atomsFromFile.rawVoiceCallRatUsagePullTimestampMillis = getWallTimeMillis();
- }
- if (atomsFromFile.voiceCallSessionPullTimestampMillis == 0L) {
- atomsFromFile.voiceCallSessionPullTimestampMillis = getWallTimeMillis();
- }
- return atomsFromFile;
+ atoms.voiceCallRatUsage =
+ sanitizeAtoms(atoms.voiceCallRatUsage, VoiceCallRatUsage.class);
+ atoms.voiceCallSession =
+ sanitizeAtoms(
+ atoms.voiceCallSession, VoiceCallSession.class, MAX_NUM_CALL_SESSIONS);
+ atoms.incomingSms = sanitizeAtoms(atoms.incomingSms, IncomingSms.class, MAX_NUM_SMS);
+ atoms.outgoingSms = sanitizeAtoms(atoms.outgoingSms, OutgoingSms.class, MAX_NUM_SMS);
+ atoms.carrierIdMismatch =
+ sanitizeAtoms(
+ atoms.carrierIdMismatch,
+ CarrierIdMismatch.class,
+ MAX_CARRIER_ID_MISMATCH);
+ atoms.dataCallSession =
+ sanitizeAtoms(
+ atoms.dataCallSession,
+ DataCallSession.class,
+ MAX_NUM_DATA_CALL_SESSIONS);
+ atoms.cellularServiceState =
+ sanitizeAtoms(
+ atoms.cellularServiceState,
+ CellularServiceState.class,
+ MAX_NUM_CELLULAR_SERVICE_STATES);
+ atoms.cellularDataServiceSwitch =
+ sanitizeAtoms(
+ atoms.cellularDataServiceSwitch,
+ CellularDataServiceSwitch.class,
+ MAX_NUM_CELLULAR_DATA_SERVICE_SWITCHES);
+ atoms.imsRegistrationStats =
+ sanitizeAtoms(
+ atoms.imsRegistrationStats,
+ ImsRegistrationStats.class,
+ MAX_NUM_IMS_REGISTRATION_STATS);
+ atoms.imsRegistrationTermination =
+ sanitizeAtoms(
+ atoms.imsRegistrationTermination,
+ ImsRegistrationTermination.class,
+ MAX_NUM_IMS_REGISTRATION_TERMINATIONS);
+ // out of caution, sanitize also the timestamps
+ atoms.voiceCallRatUsagePullTimestampMillis =
+ sanitizeTimestamp(atoms.voiceCallRatUsagePullTimestampMillis);
+ atoms.voiceCallSessionPullTimestampMillis =
+ sanitizeTimestamp(atoms.voiceCallSessionPullTimestampMillis);
+ atoms.incomingSmsPullTimestampMillis =
+ sanitizeTimestamp(atoms.incomingSmsPullTimestampMillis);
+ atoms.outgoingSmsPullTimestampMillis =
+ sanitizeTimestamp(atoms.outgoingSmsPullTimestampMillis);
+ atoms.dataCallSessionPullTimestampMillis =
+ sanitizeTimestamp(atoms.dataCallSessionPullTimestampMillis);
+ atoms.cellularServiceStatePullTimestampMillis =
+ sanitizeTimestamp(atoms.cellularServiceStatePullTimestampMillis);
+ atoms.cellularDataServiceSwitchPullTimestampMillis =
+ sanitizeTimestamp(atoms.cellularDataServiceSwitchPullTimestampMillis);
+ atoms.imsRegistrationStatsPullTimestampMillis =
+ sanitizeTimestamp(atoms.imsRegistrationStatsPullTimestampMillis);
+ atoms.imsRegistrationTerminationPullTimestampMillis =
+ sanitizeTimestamp(atoms.imsRegistrationTerminationPullTimestampMillis);
+ return atoms;
+ } catch (NoSuchFileException e) {
+ Rlog.d(TAG, "PersistAtoms file not found");
} catch (IOException | NullPointerException e) {
Rlog.e(TAG, "cannot load/parse PersistAtoms", e);
- return makeNewPersistAtoms();
}
+ return makeNewPersistAtoms();
+ }
+
+ /**
+ * Posts message to save a copy of {@link PersistAtoms} to a file after a delay.
+ *
+ * <p>The delay is introduced to avoid too frequent operations to disk, which would negatively
+ * impact the power consumption.
+ */
+ private void saveAtomsToFile() {
+ if (mSaveDelay > 0) {
+ mHandler.removeCallbacks(mSaveRunnable);
+ if (mHandler.postDelayed(mSaveRunnable, mSaveDelay)) {
+ return;
+ }
+ }
+ // In case of error posting the event or if delay is 0, save immediately
+ saveAtomsToFileNow();
}
/** Saves a copy of {@link PersistAtoms} to a file in private storage. */
- private void saveAtomsToFile() {
+ private synchronized void saveAtomsToFileNow() {
try (FileOutputStream stream = mContext.openFileOutput(FILENAME, Context.MODE_PRIVATE)) {
stream.write(PersistAtoms.toByteArray(mAtoms));
} catch (IOException e) {
@@ -164,19 +554,188 @@
}
}
+ /**
+ * Returns the service state that has the same dimension values with the given one, or {@code
+ * null} if it does not exist.
+ */
+ private @Nullable CellularServiceState find(CellularServiceState key) {
+ for (CellularServiceState state : mAtoms.cellularServiceState) {
+ if (state.voiceRat == key.voiceRat
+ && state.dataRat == key.dataRat
+ && state.voiceRoamingType == key.voiceRoamingType
+ && state.dataRoamingType == key.dataRoamingType
+ && state.isEndc == key.isEndc
+ && state.simSlotIndex == key.simSlotIndex
+ && state.isMultiSim == key.isMultiSim
+ && state.carrierId == key.carrierId) {
+ return state;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the data service switch that has the same dimension values with the given one, or
+ * {@code null} if it does not exist.
+ */
+ private @Nullable CellularDataServiceSwitch find(CellularDataServiceSwitch key) {
+ for (CellularDataServiceSwitch serviceSwitch : mAtoms.cellularDataServiceSwitch) {
+ if (serviceSwitch.ratFrom == key.ratFrom
+ && serviceSwitch.ratTo == key.ratTo
+ && serviceSwitch.simSlotIndex == key.simSlotIndex
+ && serviceSwitch.isMultiSim == key.isMultiSim
+ && serviceSwitch.carrierId == key.carrierId) {
+ return serviceSwitch;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the carrier ID mismatch event that has the same dimension values with the given one,
+ * or {@code null} if it does not exist.
+ */
+ private @Nullable CarrierIdMismatch find(CarrierIdMismatch key) {
+ for (CarrierIdMismatch mismatch : mAtoms.carrierIdMismatch) {
+ if (mismatch.mccMnc.equals(key.mccMnc)
+ && mismatch.gid1.equals(key.gid1)
+ && mismatch.spn.equals(key.spn)
+ && mismatch.pnn.equals(key.pnn)) {
+ return mismatch;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the IMS registration stats that has the same dimension values with the given one, or
+ * {@code null} if it does not exist.
+ */
+ private @Nullable ImsRegistrationStats find(ImsRegistrationStats key) {
+ for (ImsRegistrationStats stats : mAtoms.imsRegistrationStats) {
+ if (stats.carrierId == key.carrierId
+ && stats.simSlotIndex == key.simSlotIndex
+ && stats.rat == key.rat) {
+ return stats;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the IMS registration termination that has the same dimension values with the given
+ * one, or {@code null} if it does not exist.
+ */
+ private @Nullable ImsRegistrationTermination find(ImsRegistrationTermination key) {
+ for (ImsRegistrationTermination termination : mAtoms.imsRegistrationTermination) {
+ if (termination.carrierId == key.carrierId
+ && termination.isMultiSim == key.isMultiSim
+ && termination.ratAtEnd == key.ratAtEnd
+ && termination.setupFailed == key.setupFailed
+ && termination.reasonCode == key.reasonCode
+ && termination.extraCode == key.extraCode
+ && termination.extraMessage.equals(key.extraMessage)) {
+ return termination;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Inserts a new element in a random position in an array with a maximum size, replacing the
+ * least recent item if possible.
+ */
+ private static <T> T[] insertAtRandomPlace(T[] storage, T instance, int maxLength) {
+ final int newLength = storage.length + 1;
+ final boolean arrayFull = (newLength > maxLength);
+ T[] result = Arrays.copyOf(storage, arrayFull ? maxLength : newLength);
+ if (newLength == 1) {
+ result[0] = instance;
+ } else if (arrayFull) {
+ result[findItemToEvict(storage)] = instance;
+ } else {
+ // insert at random place (by moving the item at the random place to the end)
+ int insertAt = sRandom.nextInt(newLength);
+ result[newLength - 1] = result[insertAt];
+ result[insertAt] = instance;
+ }
+ return result;
+ }
+
+ /** Returns index of the item suitable for eviction when the array is full. */
+ private static <T> int findItemToEvict(T[] array) {
+ if (array instanceof CellularServiceState[]) {
+ CellularServiceState[] arr = (CellularServiceState[]) array;
+ return IntStream.range(0, arr.length)
+ .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j)
+ .getAsInt();
+ }
+
+ if (array instanceof CellularDataServiceSwitch[]) {
+ CellularDataServiceSwitch[] arr = (CellularDataServiceSwitch[]) array;
+ return IntStream.range(0, arr.length)
+ .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j)
+ .getAsInt();
+ }
+
+ if (array instanceof ImsRegistrationStats[]) {
+ ImsRegistrationStats[] arr = (ImsRegistrationStats[]) array;
+ return IntStream.range(0, arr.length)
+ .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j)
+ .getAsInt();
+ }
+
+ if (array instanceof ImsRegistrationTermination[]) {
+ ImsRegistrationTermination[] arr = (ImsRegistrationTermination[]) array;
+ return IntStream.range(0, arr.length)
+ .reduce((i, j) -> arr[i].lastUsedMillis < arr[j].lastUsedMillis ? i : j)
+ .getAsInt();
+ }
+
+ return sRandom.nextInt(array.length);
+ }
+
+ /** Sanitizes the loaded array of atoms to avoid null values. */
+ private <T> T[] sanitizeAtoms(T[] array, Class<T> cl) {
+ return ArrayUtils.emptyIfNull(array, cl);
+ }
+
+ /** Sanitizes the loaded array of atoms loaded to avoid null values and enforce max length. */
+ private <T> T[] sanitizeAtoms(T[] array, Class<T> cl, int maxLength) {
+ array = sanitizeAtoms(array, cl);
+ if (array.length > maxLength) {
+ return Arrays.copyOf(array, maxLength);
+ }
+ return array;
+ }
+
+ /** Sanitizes the timestamp of the last pull loaded from persistent storage. */
+ private long sanitizeTimestamp(long timestamp) {
+ return timestamp <= 0L ? getWallTimeMillis() : timestamp;
+ }
+
/** Returns an empty PersistAtoms with pull timestamp set to current time. */
private PersistAtoms makeNewPersistAtoms() {
PersistAtoms atoms = new PersistAtoms();
// allow pulling only after some time so data are sufficiently aggregated
- atoms.rawVoiceCallRatUsagePullTimestampMillis = getWallTimeMillis();
- atoms.voiceCallSessionPullTimestampMillis = getWallTimeMillis();
+ long currentTime = getWallTimeMillis();
+ atoms.voiceCallRatUsagePullTimestampMillis = currentTime;
+ atoms.voiceCallSessionPullTimestampMillis = currentTime;
+ atoms.incomingSmsPullTimestampMillis = currentTime;
+ atoms.outgoingSmsPullTimestampMillis = currentTime;
+ atoms.carrierIdTableVersion = TelephonyManager.UNKNOWN_CARRIER_ID_LIST_VERSION;
+ atoms.dataCallSessionPullTimestampMillis = currentTime;
+ atoms.cellularServiceStatePullTimestampMillis = currentTime;
+ atoms.cellularDataServiceSwitchPullTimestampMillis = currentTime;
+ atoms.imsRegistrationStatsPullTimestampMillis = currentTime;
+ atoms.imsRegistrationTerminationPullTimestampMillis = currentTime;
Rlog.d(TAG, "created new PersistAtoms");
return atoms;
}
@VisibleForTesting
protected long getWallTimeMillis() {
- // epoch time in UTC, preserved across reboots, but can be adjusted e.g. by the user or NTP
+ // Epoch time in UTC, preserved across reboots, but can be adjusted e.g. by the user or NTP
return System.currentTimeMillis();
}
}
diff --git a/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
new file mode 100644
index 0000000..9cbc406
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/ServiceStateStats.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2020 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 android.annotation.Nullable;
+import android.os.SystemClock;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+
+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.CellularDataServiceSwitch;
+import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
+import com.android.telephony.Rlog;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/** Tracks service state duration and switch metrics for each phone. */
+public class ServiceStateStats {
+ private static final String TAG = ServiceStateStats.class.getSimpleName();
+
+ private final AtomicReference<TimestampedServiceState> mLastState =
+ new AtomicReference<>(new TimestampedServiceState(null, 0L));
+ private final Phone mPhone;
+ private final PersistAtomsStorage mStorage;
+
+ public ServiceStateStats(Phone phone) {
+ mPhone = phone;
+ mStorage = PhoneFactory.getMetricsCollector().getAtomsStorage();
+ }
+
+ /** Finalizes the durations of the current service state segment. */
+ public void conclude() {
+ final long now = getTimeMillis();
+ TimestampedServiceState lastState =
+ mLastState.getAndUpdate(
+ state -> new TimestampedServiceState(state.mServiceState, now));
+ addServiceState(lastState, now);
+ }
+
+ /** Updates the current service state. */
+ public void onServiceStateChanged(ServiceState serviceState) {
+ final long now = getTimeMillis();
+ if (isModemOff(serviceState)) {
+ // Finish the duration of last service state and mark modem off
+ addServiceState(mLastState.getAndSet(new TimestampedServiceState(null, now)), now);
+ } else {
+ CellularServiceState newState = new CellularServiceState();
+ newState.voiceRat = getVoiceRat(mPhone, serviceState);
+ newState.dataRat = getDataRat(serviceState);
+ newState.voiceRoamingType = serviceState.getVoiceRoamingType();
+ newState.dataRoamingType = serviceState.getDataRoamingType();
+ newState.isEndc = isEndc(serviceState);
+ newState.simSlotIndex = mPhone.getPhoneId();
+ newState.isMultiSim = SimSlotState.isMultiSim();
+ newState.carrierId = mPhone.getCarrierId();
+
+ TimestampedServiceState prevState =
+ mLastState.getAndSet(new TimestampedServiceState(newState, now));
+ addServiceStateAndSwitch(
+ prevState, now, getDataServiceSwitch(prevState.mServiceState, newState));
+ }
+ }
+
+ private void addServiceState(TimestampedServiceState prevState, long now) {
+ addServiceStateAndSwitch(prevState, now, null);
+ }
+
+ private void addServiceStateAndSwitch(
+ TimestampedServiceState prevState,
+ long now,
+ @Nullable CellularDataServiceSwitch serviceSwitch) {
+ if (prevState.mServiceState == null) {
+ // Skip duration when modem is off
+ return;
+ }
+ if (now >= prevState.mTimestamp) {
+ CellularServiceState state = copyOf(prevState.mServiceState);
+ state.totalTimeMillis = now - prevState.mTimestamp;
+ mStorage.addCellularServiceStateAndCellularDataServiceSwitch(state, serviceSwitch);
+ } else {
+ Rlog.e(TAG, "addServiceState: durationMillis<0");
+ }
+ }
+
+ @Nullable
+ private CellularDataServiceSwitch getDataServiceSwitch(
+ @Nullable CellularServiceState prevState, CellularServiceState nextState) {
+ // Record switch only if multi-SIM state and carrier ID are the same and data RAT differs.
+ if (prevState != null
+ && prevState.isMultiSim == nextState.isMultiSim
+ && prevState.carrierId == nextState.carrierId
+ && prevState.dataRat != nextState.dataRat) {
+ CellularDataServiceSwitch serviceSwitch = new CellularDataServiceSwitch();
+ serviceSwitch.ratFrom = prevState.dataRat;
+ serviceSwitch.ratTo = nextState.dataRat;
+ serviceSwitch.isMultiSim = nextState.isMultiSim;
+ serviceSwitch.simSlotIndex = nextState.simSlotIndex;
+ serviceSwitch.carrierId = nextState.carrierId;
+ serviceSwitch.switchCount = 1;
+ return serviceSwitch;
+ } else {
+ return null;
+ }
+ }
+
+ private static CellularServiceState copyOf(CellularServiceState state) {
+ // MessageNano does not support clone, have to copy manually
+ CellularServiceState copy = new CellularServiceState();
+ copy.voiceRat = state.voiceRat;
+ copy.dataRat = state.dataRat;
+ copy.voiceRoamingType = state.voiceRoamingType;
+ copy.dataRoamingType = state.dataRoamingType;
+ copy.isEndc = state.isEndc;
+ copy.simSlotIndex = state.simSlotIndex;
+ copy.isMultiSim = state.isMultiSim;
+ copy.carrierId = state.carrierId;
+ copy.totalTimeMillis = state.totalTimeMillis;
+ return copy;
+ }
+
+ private static boolean isModemOff(ServiceState state) {
+ // NOTE: Wifi calls can be made in airplane mode, where voice reg state is POWER_OFF but
+ // data reg state is IN_SERVICE. In this case, service state should still be tracked.
+ return state.getVoiceRegState() == ServiceState.STATE_POWER_OFF
+ && state.getDataRegState() == ServiceState.STATE_POWER_OFF;
+ }
+
+ private static @NetworkType int getVoiceRat(Phone phone, ServiceState state) {
+ boolean isWifiCall =
+ phone.getImsPhone() != null
+ && phone.getImsPhone().isWifiCallingEnabled()
+ && state.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_IWLAN;
+ return isWifiCall ? TelephonyManager.NETWORK_TYPE_IWLAN : state.getVoiceNetworkType();
+ }
+
+ private static @NetworkType int getDataRat(ServiceState state) {
+ final NetworkRegistrationInfo wwanRegInfo =
+ state.getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ return wwanRegInfo != null
+ ? wwanRegInfo.getAccessNetworkTechnology()
+ : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+
+ private static boolean isEndc(ServiceState state) {
+ if (getDataRat(state) != TelephonyManager.NETWORK_TYPE_LTE) {
+ return false;
+ }
+ int nrState = state.getNrState();
+ return nrState == NetworkRegistrationInfo.NR_STATE_CONNECTED
+ || nrState == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED;
+ }
+
+ @VisibleForTesting
+ protected long getTimeMillis() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ private static final class TimestampedServiceState {
+ private final CellularServiceState mServiceState;
+ private final long mTimestamp; // Start time of the service state segment
+
+ TimestampedServiceState(CellularServiceState serviceState, long timestamp) {
+ mServiceState = serviceState;
+ mTimestamp = timestamp;
+ }
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/SimSlotState.java b/src/java/com/android/internal/telephony/metrics/SimSlotState.java
index 3c3ae62..95fa042 100644
--- a/src/java/com/android/internal/telephony/metrics/SimSlotState.java
+++ b/src/java/com/android/internal/telephony/metrics/SimSlotState.java
@@ -20,9 +20,12 @@
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.uicc.UiccSlot;
+import com.android.telephony.Rlog;
/** Snapshots and stores the current SIM state. */
public class SimSlotState {
+ private static final String TAG = SimSlotState.class.getSimpleName();
+
public final int numActiveSlots;
public final int numActiveSims;
public final int numActiveEsims;
@@ -62,4 +65,21 @@
this.numActiveSims = numActiveSims;
this.numActiveEsims = numActiveEsims;
}
+
+ /** Returns whether the given phone is using a eSIM. */
+ public static boolean isEsim(int phoneId) {
+ UiccSlot slot = UiccController.getInstance().getUiccSlotForPhone(phoneId);
+ if (slot != null) {
+ return slot.isEuicc();
+ } else {
+ // should not happen, but assume we are not using eSIM
+ Rlog.e(TAG, "isEsim: slot=null for phone " + phoneId);
+ return false;
+ }
+ }
+
+ /** Returns whether the device has multiple active SIM profiles. */
+ public static boolean isMultiSim() {
+ return (getCurrentState().numActiveSims > 1);
+ }
}
diff --git a/src/java/com/android/internal/telephony/metrics/SmsStats.java b/src/java/com/android/internal/telephony/metrics/SmsStats.java
new file mode 100644
index 0000000..44182c8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/metrics/SmsStats.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2020 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 com.android.internal.telephony.InboundSmsHandler.SOURCE_INJECTED_FROM_IMS;
+import static com.android.internal.telephony.InboundSmsHandler.SOURCE_INJECTED_FROM_UNKNOWN;
+import static com.android.internal.telephony.InboundSmsHandler.SOURCE_NOT_INJECTED;
+import static com.android.internal.telephony.SmsResponse.NO_ERROR_CODE;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_ERROR_GENERIC;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_ERROR_NOT_SUPPORTED;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_ERROR_NO_MEMORY;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__ERROR__SMS_SUCCESS;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_FORMAT__SMS_FORMAT_3GPP;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_FORMAT__SMS_FORMAT_3GPP2;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TECH__SMS_TECH_CS_3GPP;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TECH__SMS_TECH_CS_3GPP2;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TECH__SMS_TECH_IMS;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TECH__SMS_TECH_UNKNOWN;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TYPE__SMS_TYPE_NORMAL;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TYPE__SMS_TYPE_SMS_PP;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TYPE__SMS_TYPE_VOICEMAIL_INDICATION;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TYPE__SMS_TYPE_WAP_PUSH;
+import static com.android.internal.telephony.TelephonyStatsLog.INCOMING_SMS__SMS_TYPE__SMS_TYPE_ZERO;
+import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR;
+import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_FALLBACK;
+import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY;
+import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_SUCCESS;
+import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_UNKNOWN;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.ServiceState;
+import android.telephony.SmsManager;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase;
+import android.telephony.ims.stub.ImsSmsImplBase.SendStatusResult;
+
+import com.android.internal.telephony.InboundSmsHandler;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.nano.PersistAtomsProto.IncomingSms;
+import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
+import com.android.telephony.Rlog;
+
+import java.util.Random;
+
+/** Collects voice call events per phone ID for the pulled atom. */
+public class SmsStats {
+ private static final String TAG = SmsStats.class.getSimpleName();
+
+ /** 3GPP error for out of service: "no network service" in TS 27.005 cl 3.2.5 */
+ private static final int NO_NETWORK_ERROR_3GPP = 331;
+
+ /** 3GPP2 error for out of service: "Other radio interface problem" in N.S0005 Table 171 */
+ private static final int NO_NETWORK_ERROR_3GPP2 = 66;
+
+ private final Phone mPhone;
+
+ private final PersistAtomsStorage mAtomsStorage =
+ PhoneFactory.getMetricsCollector().getAtomsStorage();
+
+ private static final Random RANDOM = new Random();
+
+ public SmsStats(Phone phone) {
+ mPhone = phone;
+ }
+
+ /** Create a new atom when multi-part incoming SMS is dropped due to missing parts. */
+ public void onDroppedIncomingMultipartSms(boolean is3gpp2, int receivedCount, int totalCount) {
+ IncomingSms proto = getIncomingDefaultProto(is3gpp2, SOURCE_NOT_INJECTED);
+ // Keep SMS tech as unknown because it's possible that it changed overtime and is not
+ // necessarily the current one. Similarly mark the RAT as unknown.
+ proto.smsTech = INCOMING_SMS__SMS_TECH__SMS_TECH_UNKNOWN;
+ proto.rat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ proto.error = INCOMING_SMS__ERROR__SMS_ERROR_GENERIC;
+ proto.totalParts = totalCount;
+ proto.receivedParts = receivedCount;
+ mAtomsStorage.addIncomingSms(proto);
+ }
+
+ /** Create a new atom when an SMS for the voicemail indicator is received. */
+ public void onIncomingSmsVoicemail(boolean is3gpp2,
+ @InboundSmsHandler.SmsSource int smsSource) {
+ IncomingSms proto = getIncomingDefaultProto(is3gpp2, smsSource);
+ proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_VOICEMAIL_INDICATION;
+ mAtomsStorage.addIncomingSms(proto);
+ }
+
+ /** Create a new atom when an SMS of type zero is received. */
+ public void onIncomingSmsTypeZero(@InboundSmsHandler.SmsSource int smsSource) {
+ IncomingSms proto = getIncomingDefaultProto(false /* is3gpp2 */, smsSource);
+ proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_ZERO;
+ mAtomsStorage.addIncomingSms(proto);
+ }
+
+ /** Create a new atom when an SMS-PP for the SIM card is received. */
+ public void onIncomingSmsPP(@InboundSmsHandler.SmsSource int smsSource, boolean success) {
+ IncomingSms proto = getIncomingDefaultProto(false /* is3gpp2 */, smsSource);
+ proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_SMS_PP;
+ proto.error = getIncomingSmsError(success);
+ mAtomsStorage.addIncomingSms(proto);
+ }
+
+ /** Create a new atom when an SMS is received successfully. */
+ public void onIncomingSmsSuccess(boolean is3gpp2,
+ @InboundSmsHandler.SmsSource int smsSource, int messageCount,
+ boolean blocked, long messageId) {
+ IncomingSms proto = getIncomingDefaultProto(is3gpp2, smsSource);
+ proto.totalParts = messageCount;
+ proto.receivedParts = messageCount;
+ proto.blocked = blocked;
+ proto.messageId = messageId;
+ mAtomsStorage.addIncomingSms(proto);
+ }
+
+ /** Create a new atom when an incoming SMS has an error. */
+ public void onIncomingSmsError(boolean is3gpp2,
+ @InboundSmsHandler.SmsSource int smsSource, int result) {
+ IncomingSms proto = getIncomingDefaultProto(is3gpp2, smsSource);
+ proto.error = getIncomingSmsError(result);
+ mAtomsStorage.addIncomingSms(proto);
+ }
+
+ /** Create a new atom when an incoming WAP_PUSH SMS is received. */
+ public void onIncomingSmsWapPush(@InboundSmsHandler.SmsSource int smsSource,
+ int messageCount, int result, long messageId) {
+ IncomingSms proto = getIncomingDefaultProto(false, smsSource);
+ proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_WAP_PUSH;
+ proto.totalParts = messageCount;
+ proto.receivedParts = messageCount;
+ proto.error = getIncomingSmsError(result);
+ proto.messageId = messageId;
+ mAtomsStorage.addIncomingSms(proto);
+ }
+
+ /** Create a new atom when an outgoing SMS is sent. */
+ public void onOutgoingSms(boolean isOverIms, boolean is3gpp2, boolean fallbackToCs,
+ @SmsManager.Result int errorCode, long messageId, boolean isFromDefaultApp) {
+ onOutgoingSms(isOverIms, is3gpp2, fallbackToCs, errorCode, NO_ERROR_CODE,
+ messageId, isFromDefaultApp);
+ }
+
+ /** Create a new atom when an outgoing SMS is sent. */
+ public void onOutgoingSms(boolean isOverIms, boolean is3gpp2, boolean fallbackToCs,
+ @SmsManager.Result int errorCode, int radioSpecificErrorCode, long messageId,
+ boolean isFromDefaultApp) {
+ OutgoingSms proto =
+ getOutgoingDefaultProto(is3gpp2, isOverIms, messageId, isFromDefaultApp);
+
+ if (isOverIms) {
+ // Populate error code and result for IMS case
+ proto.errorCode = errorCode;
+ if (fallbackToCs) {
+ proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_FALLBACK;
+ } else if (errorCode == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY) {
+ proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY;
+ } else if (errorCode != SmsManager.RESULT_ERROR_NONE) {
+ proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR;
+ }
+ } else {
+ // Populate error code and result for CS case
+ if (errorCode == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY) {
+ proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY;
+ } else if (errorCode != SmsManager.RESULT_ERROR_NONE) {
+ proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR;
+ }
+ proto.errorCode = radioSpecificErrorCode;
+ if (errorCode == SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE
+ && radioSpecificErrorCode == NO_ERROR_CODE) {
+ proto.errorCode = is3gpp2 ? NO_NETWORK_ERROR_3GPP2 : NO_NETWORK_ERROR_3GPP;
+ }
+ }
+ mAtomsStorage.addOutgoingSms(proto);
+ }
+
+ /** Creates a proto for a normal single-part {@code IncomingSms} with default values. */
+ private IncomingSms getIncomingDefaultProto(boolean is3gpp2,
+ @InboundSmsHandler.SmsSource int smsSource) {
+ IncomingSms proto = new IncomingSms();
+ proto.smsFormat = getSmsFormat(is3gpp2);
+ proto.smsTech = getSmsTech(smsSource, is3gpp2);
+ proto.rat = getRat(smsSource);
+ proto.smsType = INCOMING_SMS__SMS_TYPE__SMS_TYPE_NORMAL;
+ proto.totalParts = 1;
+ proto.receivedParts = 1;
+ proto.blocked = false;
+ proto.error = INCOMING_SMS__ERROR__SMS_SUCCESS;
+ proto.isRoaming = getIsRoaming();
+ proto.simSlotIndex = getPhoneId();
+ proto.isMultiSim = SimSlotState.isMultiSim();
+ proto.isEsim = SimSlotState.isEsim(getPhoneId());
+ proto.carrierId = getCarrierId();
+ // Message ID is initialized with random number, as it is not available for all incoming
+ // SMS messages (e.g. those handled by OS or error cases).
+ proto.messageId = RANDOM.nextLong();
+ return proto;
+ }
+
+ /** Create a proto for a normal {@code OutgoingSms} with default values. */
+ private OutgoingSms getOutgoingDefaultProto(boolean is3gpp2, boolean isOverIms,
+ long messageId, boolean isFromDefaultApp) {
+ OutgoingSms proto = new OutgoingSms();
+ proto.smsFormat = getSmsFormat(is3gpp2);
+ proto.smsTech = getSmsTech(isOverIms, is3gpp2);
+ proto.rat = getRat(isOverIms);
+ proto.sendResult = OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_SUCCESS;
+ proto.errorCode = isOverIms ? SmsManager.RESULT_ERROR_NONE : NO_ERROR_CODE;
+ proto.isRoaming = getIsRoaming();
+ proto.isFromDefaultApp = isFromDefaultApp;
+ proto.simSlotIndex = getPhoneId();
+ proto.isMultiSim = SimSlotState.isMultiSim();
+ proto.isEsim = SimSlotState.isEsim(getPhoneId());
+ proto.carrierId = getCarrierId();
+ // If the message ID is invalid, generate a random value
+ proto.messageId = messageId != 0L ? messageId : RANDOM.nextLong();
+ // Setting the retry ID to zero. If needed, it will be incremented when the atom is added
+ // in the persistent storage.
+ proto.retryId = 0;
+ return proto;
+ }
+
+ private static int getSmsFormat(boolean is3gpp2) {
+ if (is3gpp2) {
+ return INCOMING_SMS__SMS_FORMAT__SMS_FORMAT_3GPP2;
+ } else {
+ return INCOMING_SMS__SMS_FORMAT__SMS_FORMAT_3GPP;
+ }
+ }
+
+ private int getSmsTech(@InboundSmsHandler.SmsSource int smsSource, boolean is3gpp2) {
+ if (smsSource == SOURCE_INJECTED_FROM_UNKNOWN) {
+ return INCOMING_SMS__SMS_TECH__SMS_TECH_UNKNOWN;
+ }
+ return getSmsTech(smsSource == SOURCE_INJECTED_FROM_IMS, is3gpp2);
+ }
+
+ private int getSmsTech(boolean isOverIms, boolean is3gpp2) {
+ if (isOverIms) {
+ return INCOMING_SMS__SMS_TECH__SMS_TECH_IMS;
+ } else if (is3gpp2) {
+ return INCOMING_SMS__SMS_TECH__SMS_TECH_CS_3GPP2;
+ } else {
+ return INCOMING_SMS__SMS_TECH__SMS_TECH_CS_3GPP;
+ }
+ }
+
+ private static int getIncomingSmsError(int result) {
+ switch (result) {
+ case Activity.RESULT_OK:
+ case Intents.RESULT_SMS_HANDLED:
+ return INCOMING_SMS__ERROR__SMS_SUCCESS;
+ case Intents.RESULT_SMS_OUT_OF_MEMORY:
+ return INCOMING_SMS__ERROR__SMS_ERROR_NO_MEMORY;
+ case Intents.RESULT_SMS_UNSUPPORTED:
+ return INCOMING_SMS__ERROR__SMS_ERROR_NOT_SUPPORTED;
+ case Intents.RESULT_SMS_GENERIC_ERROR:
+ default:
+ return INCOMING_SMS__ERROR__SMS_ERROR_GENERIC;
+ }
+ }
+
+ private static int getIncomingSmsError(boolean success) {
+ if (success) {
+ return INCOMING_SMS__ERROR__SMS_SUCCESS;
+ } else {
+ return INCOMING_SMS__ERROR__SMS_ERROR_GENERIC;
+ }
+ }
+
+ private static int getOutgoingSmsError(@SendStatusResult int imsSendResult) {
+ switch (imsSendResult) {
+ case ImsSmsImplBase.SEND_STATUS_OK:
+ return OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_SUCCESS;
+ case ImsSmsImplBase.SEND_STATUS_ERROR:
+ return OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR;
+ case ImsSmsImplBase.SEND_STATUS_ERROR_RETRY:
+ return OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_RETRY;
+ case ImsSmsImplBase.SEND_STATUS_ERROR_FALLBACK:
+ return OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_ERROR_FALLBACK;
+ default:
+ return OUTGOING_SMS__SEND_RESULT__SMS_SEND_RESULT_UNKNOWN;
+ }
+ }
+
+ private int getPhoneId() {
+ Phone phone = mPhone;
+ if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ phone = mPhone.getDefaultPhone();
+ }
+ return phone.getPhoneId();
+ }
+
+ @Nullable
+ private ServiceState getServiceState() {
+ Phone phone = mPhone;
+ if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ phone = mPhone.getDefaultPhone();
+ }
+ ServiceStateTracker serviceStateTracker = phone.getServiceStateTracker();
+ return serviceStateTracker != null ? serviceStateTracker.getServiceState() : null;
+ }
+
+ private @NetworkType int getRat(@InboundSmsHandler.SmsSource int smsSource) {
+ if (smsSource == SOURCE_INJECTED_FROM_UNKNOWN) {
+ return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+ return getRat(smsSource == SOURCE_INJECTED_FROM_IMS);
+ }
+
+ private @NetworkType int getRat(boolean isOverIms) {
+ if (isOverIms) {
+ if (mPhone.getImsRegistrationTech()
+ == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN) {
+ return TelephonyManager.NETWORK_TYPE_IWLAN;
+ }
+ }
+ // TODO(b/168837897): Returns the RAT at the time the SMS was received..
+ ServiceState serviceState = getServiceState();
+ return serviceState != null
+ ? serviceState.getVoiceNetworkType() : TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ }
+
+ private boolean getIsRoaming() {
+ ServiceState serviceState = getServiceState();
+ return serviceState != null ? serviceState.getRoaming() : false;
+ }
+
+ private int getCarrierId() {
+ Phone phone = mPhone;
+ if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ phone = mPhone.getDefaultPhone();
+ }
+ return phone.getCarrierId();
+ }
+
+ private void loge(String format, Object... args) {
+ Rlog.e(TAG, "[" + mPhone.getPhoneId() + "]" + String.format(format, args));
+ }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index fa75bc8..14ab36c 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -18,6 +18,7 @@
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static com.android.internal.telephony.InboundSmsHandler.SOURCE_NOT_INJECTED;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ANSWER;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_SEND_SMS;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
@@ -72,6 +73,7 @@
import com.android.internal.telephony.CarrierResolver;
import com.android.internal.telephony.DriverCall;
import com.android.internal.telephony.GsmCdmaConnection;
+import com.android.internal.telephony.InboundSmsHandler;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.RIL;
import com.android.internal.telephony.RILConstants;
@@ -2407,18 +2409,18 @@
*
* @param phoneId Phone id
* @param type Type of the SMS.
- * @param smsOverIms true if the SMS was received over SMS, false otherwise
+ * @param smsSource the source of the SMS message
* @param format SMS format. Either 3GPP or 3GPP2.
* @param timestamps array with timestamps of each incoming SMS part. It contains a single
* @param blocked indicates if the message was blocked or not.
* @param success Indicates if the SMS-PP was successfully delivered to the USIM.
* @param messageId Unique id for this message.
*/
- private void writeIncomingSmsSessionWithType(int phoneId, int type, boolean smsOverIms,
- String format, long[] timestamps, boolean blocked, boolean success,
- long messageId) {
+ private void writeIncomingSmsSessionWithType(int phoneId, int type,
+ @InboundSmsHandler.SmsSource int smsSource, String format, long[] timestamps,
+ boolean blocked, boolean success, long messageId) {
logv("Logged SMS session consisting of " + timestamps.length
- + " parts, over IMS = " + smsOverIms
+ + " parts, source = " + smsSource
+ " blocked = " + blocked
+ " type = " + type
+ " messageId = " + messageId);
@@ -2428,8 +2430,9 @@
SmsSessionEventBuilder eventBuilder =
new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED)
.setFormat(convertSmsFormat(format))
- .setTech(smsOverIms ? SmsSession.Event.Tech.SMS_IMS :
- SmsSession.Event.Tech.SMS_GSM)
+ .setTech(smsSource != SOURCE_NOT_INJECTED
+ ? SmsSession.Event.Tech.SMS_IMS
+ : SmsSession.Event.Tech.SMS_GSM)
.setErrorCode(success ? SmsManager.RESULT_ERROR_NONE :
SmsManager.RESULT_ERROR_GENERIC_FAILURE)
.setSmsType(type)
@@ -2444,42 +2447,43 @@
* Write an incoming WAP-PUSH message.
*
* @param phoneId Phone id
- * @param smsOverIms true if the SMS was received over SMS, false otherwise
+ * @param smsSource the source of the SMS message
* @param format SMS format. Either 3GPP or 3GPP2.
* @param timestamps array with timestamps of each incoming SMS part. It contains a single
* @param success Indicates if the SMS-PP was successfully delivered to the USIM.
* @param messageId Unique id for this message.
*/
- public void writeIncomingWapPush(int phoneId, boolean smsOverIms, String format,
- long[] timestamps, boolean success, long messageId) {
+ public void writeIncomingWapPush(int phoneId, @InboundSmsHandler.SmsSource int smsSource,
+ String format, long[] timestamps, boolean success, long messageId) {
writeIncomingSmsSessionWithType(phoneId, SmsSession.Event.SmsType.SMS_TYPE_WAP_PUSH,
- smsOverIms, format, timestamps, false, success, messageId);
+ smsSource, format, timestamps, false, success, messageId);
}
/**
* Write a successful incoming SMS session
*
* @param phoneId Phone id
- * @param smsOverIms true if the SMS was received over SMS, false otherwise
+ * @param smsSource the source of the SMS message
* @param format SMS format. Either 3GPP or 3GPP2.
* @param timestamps array with timestamps of each incoming SMS part. It contains a single
* @param blocked indicates if the message was blocked or not.
* @param messageId Unique id for this message.
*/
- public void writeIncomingSmsSession(int phoneId, boolean smsOverIms, String format,
- long[] timestamps, boolean blocked, long messageId) {
+ public void writeIncomingSmsSession(int phoneId, @InboundSmsHandler.SmsSource int smsSource,
+ String format, long[] timestamps, boolean blocked, long messageId) {
writeIncomingSmsSessionWithType(phoneId, SmsSession.Event.SmsType.SMS_TYPE_NORMAL,
- smsOverIms, format, timestamps, blocked, true, messageId);
+ smsSource, format, timestamps, blocked, true, messageId);
}
/**
* Write an error incoming SMS
*
* @param phoneId Phone id
- * @param smsOverIms true if the SMS was received over SMS, false otherwise
+ * @param smsSource the source of the SMS message
* @param result Indicates the reason of the failure.
*/
- public void writeIncomingSmsError(int phoneId, boolean smsOverIms, int result) {
+ public void writeIncomingSmsError(int phoneId, @InboundSmsHandler.SmsSource int smsSource,
+ int result) {
logv("Incoming SMS error = " + result);
int smsError = SmsManager.RESULT_ERROR_GENERIC_FAILURE;
@@ -2504,8 +2508,9 @@
SmsSessionEventBuilder eventBuilder =
new SmsSessionEventBuilder(SmsSession.Event.Type.SMS_RECEIVED)
.setErrorCode(smsError)
- .setTech(smsOverIms ? SmsSession.Event.Tech.SMS_IMS :
- SmsSession.Event.Tech.SMS_GSM);
+ .setTech(smsSource != SOURCE_NOT_INJECTED
+ ? SmsSession.Event.Tech.SMS_IMS
+ : SmsSession.Event.Tech.SMS_GSM);
smsSession.addEvent(eventBuilder);
finishSmsSession(smsSession);
}
diff --git a/src/java/com/android/internal/telephony/metrics/VoiceCallRatTracker.java b/src/java/com/android/internal/telephony/metrics/VoiceCallRatTracker.java
index 7d7cd0e..5183ea4 100644
--- a/src/java/com/android/internal/telephony/metrics/VoiceCallRatTracker.java
+++ b/src/java/com/android/internal/telephony/metrics/VoiceCallRatTracker.java
@@ -16,7 +16,7 @@
package com.android.internal.telephony.metrics;
-import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage;
+import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
import com.android.telephony.Rlog;
import java.util.Arrays;
@@ -52,7 +52,7 @@
}
/** Creates an RAT tracker from saved atoms at startup. */
- public static VoiceCallRatTracker fromProto(RawVoiceCallRatUsage[] usages) {
+ public static VoiceCallRatTracker fromProto(VoiceCallRatUsage[] usages) {
VoiceCallRatTracker tracker = new VoiceCallRatTracker();
if (usages == null) {
Rlog.e(TAG, "fromProto: usages=null");
@@ -63,10 +63,10 @@
}
/** Append the map to javanano persist atoms. */
- public RawVoiceCallRatUsage[] toProto() {
+ public VoiceCallRatUsage[] toProto() {
return mRatUsageMap.entrySet().stream()
.map(VoiceCallRatTracker::entryToProto)
- .toArray(RawVoiceCallRatUsage[]::new);
+ .toArray(VoiceCallRatUsage[]::new);
}
/** Resets the tracker. */
@@ -140,14 +140,14 @@
}
}
- private void addProto(RawVoiceCallRatUsage usage) {
+ private void addProto(VoiceCallRatUsage usage) {
mRatUsageMap.put(Key.fromProto(usage), Value.fromProto(usage));
}
- private static RawVoiceCallRatUsage entryToProto(Map.Entry<Key, Value> entry) {
+ private static VoiceCallRatUsage entryToProto(Map.Entry<Key, Value> entry) {
Key key = entry.getKey();
Value value = entry.getValue();
- RawVoiceCallRatUsage usage = new RawVoiceCallRatUsage();
+ VoiceCallRatUsage usage = new VoiceCallRatUsage();
usage.carrierId = key.carrierId;
usage.rat = key.rat;
if (value.mConnectionIds != null) {
@@ -172,7 +172,7 @@
this.rat = rat;
}
- static Key fromProto(RawVoiceCallRatUsage usage) {
+ static Key fromProto(VoiceCallRatUsage usage) {
return new Key(usage.carrierId, usage.rat);
}
@@ -226,7 +226,7 @@
}
}
- static Value fromProto(RawVoiceCallRatUsage usage) {
+ static Value fromProto(VoiceCallRatUsage usage) {
Value value = new Value(usage.totalDurationMillis, usage.callCount);
return value;
}
diff --git a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
index 927a67e..6b6da10 100644
--- a/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
+++ b/src/java/com/android/internal/telephony/metrics/VoiceCallSessionStats.java
@@ -21,6 +21,11 @@
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_UNKNOWN;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_FULLBAND;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_WIDEBAND;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_SLOW;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_FAST;
@@ -31,18 +36,26 @@
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_GREAT;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
import android.annotation.Nullable;
+import android.content.Context;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
import android.os.SystemClock;
+import android.telecom.VideoProfile;
+import android.telecom.VideoProfile.VideoState;
+import android.telephony.AccessNetworkUtils;
import android.telephony.Annotation.NetworkType;
import android.telephony.DisconnectCause;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
+import android.util.LongSparseArray;
import android.util.SparseArray;
import android.util.SparseIntArray;
-import android.util.SparseLongArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Call;
@@ -57,10 +70,10 @@
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.AudioCodec;
import com.android.internal.telephony.uicc.UiccController;
-import com.android.internal.telephony.uicc.UiccSlot;
import com.android.telephony.Rlog;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -70,28 +83,36 @@
public class VoiceCallSessionStats {
private static final String TAG = VoiceCallSessionStats.class.getSimpleName();
- /** Bitmask value of unknown audio codecs. */
- private static final long AUDIO_CODEC_UNKNOWN = 1L << AudioCodec.AUDIO_CODEC_UNKNOWN;
-
/** Upper bounds of each call setup duration category in milliseconds. */
private static final int CALL_SETUP_DURATION_UNKNOWN = 0;
- private static final int CALL_SETUP_DURATION_EXTREMELY_FAST = 60;
- private static final int CALL_SETUP_DURATION_ULTRA_FAST = 100;
- private static final int CALL_SETUP_DURATION_VERY_FAST = 300;
- private static final int CALL_SETUP_DURATION_FAST = 600;
- private static final int CALL_SETUP_DURATION_NORMAL = 1000;
- private static final int CALL_SETUP_DURATION_SLOW = 3000;
+ private static final int CALL_SETUP_DURATION_EXTREMELY_FAST = 400;
+ private static final int CALL_SETUP_DURATION_ULTRA_FAST = 700;
+ private static final int CALL_SETUP_DURATION_VERY_FAST = 1000;
+ private static final int CALL_SETUP_DURATION_FAST = 1500;
+ private static final int CALL_SETUP_DURATION_NORMAL = 2500;
+ private static final int CALL_SETUP_DURATION_SLOW = 4000;
private static final int CALL_SETUP_DURATION_VERY_SLOW = 6000;
private static final int CALL_SETUP_DURATION_ULTRA_SLOW = 10000;
// CALL_SETUP_DURATION_EXTREMELY_SLOW has no upper bound (it includes everything above 10000)
- /** Holds the audio codec bitmask value for CS calls. */
- private static final SparseLongArray CS_CODEC_MAP = buildGsmCdmaCodecMap();
+ /** Number of buckets for codec quality, from UNKNOWN to FULLBAND. */
+ private static final int CODEC_QUALITY_COUNT = 5;
- /** Holds the audio codec bitmask value for IMS calls. */
- private static final SparseLongArray IMS_CODEC_MAP = buildImsCodecMap();
+ /**
+ * Threshold to calculate the main audio codec quality of the call.
+ *
+ * The audio codec quality was equal to or greater than the main audio codec quality for
+ * at least 70% of the call.
+ */
+ private static final int MAIN_CODEC_QUALITY_THRESHOLD = 70;
- /** Holds setup duration buckets with keys as their lower bounds in milliseconds. */
+ /** Holds the audio codec value for CS calls. */
+ private static final SparseIntArray CS_CODEC_MAP = buildGsmCdmaCodecMap();
+
+ /** Holds the audio codec value for IMS calls. */
+ private static final SparseIntArray IMS_CODEC_MAP = buildImsCodecMap();
+
+ /** Holds setup duration buckets with values as their upper bounds in milliseconds. */
private static final SparseIntArray CALL_SETUP_DURATION_MAP = buildCallSetupDurationMap();
/**
@@ -101,6 +122,13 @@
private final SparseArray<VoiceCallSession> mCallProtos = new SparseArray<>();
/**
+ * Tracks usage of codecs for each call. The outer array is used to map each connection id to
+ * the corresponding codec usage. The inner array is used to map timestamp (key) with the
+ * codec in use (value).
+ */
+ private final SparseArray<LongSparseArray<Integer>> mCodecUsage = new SparseArray<>();
+
+ /**
* Tracks call RAT usage.
*
* <p>RAT usage is mainly tied to phones rather than calls, since each phone can have multiple
@@ -224,12 +252,51 @@
/** Updates internal states when audio codec for a call is changed. */
public synchronized void onAudioCodecChanged(Connection conn, int audioQuality) {
- VoiceCallSession proto = mCallProtos.get(getConnectionId(conn));
+ int id = getConnectionId(conn);
+ VoiceCallSession proto = mCallProtos.get(id);
if (proto == null) {
loge("onAudioCodecChanged: untracked connection");
return;
}
- proto.codecBitmask |= audioQualityToCodecBitmask(proto.bearerAtEnd, audioQuality);
+ int codec = audioQualityToCodec(proto.bearerAtEnd, audioQuality);
+ proto.codecBitmask |= (1L << codec);
+
+ if (mCodecUsage.contains(id)) {
+ mCodecUsage.get(id).append(getTimeMillis(), codec);
+ } else {
+ LongSparseArray<Integer> arr = new LongSparseArray<>();
+ arr.append(getTimeMillis(), codec);
+ mCodecUsage.put(id, arr);
+ }
+ }
+
+ /** Updates internal states when video state changes. */
+ public synchronized void onVideoStateChange(
+ ImsPhoneConnection conn, @VideoState int videoState) {
+ int id = getConnectionId(conn);
+ VoiceCallSession proto = mCallProtos.get(id);
+ if (proto == null) {
+ loge("onVideoStateChange: untracked connection");
+ return;
+ }
+ logd(TAG, "Video state = " + videoState);
+ if (videoState != VideoProfile.STATE_AUDIO_ONLY) {
+ proto.videoEnabled = true;
+ }
+ }
+
+ /** Updates internal states when multiparty state changes. */
+ public synchronized void onMultipartyChange(ImsPhoneConnection conn, boolean isMultiParty) {
+ int id = getConnectionId(conn);
+ VoiceCallSession proto = mCallProtos.get(id);
+ if (proto == null) {
+ loge("onMultipartyChange: untracked connection");
+ return;
+ }
+ logd(TAG, "Multiparty = " + isMultiParty);
+ if (isMultiParty) {
+ proto.isMultiparty = true;
+ }
}
/**
@@ -316,7 +383,7 @@
} else {
int bearer = getBearer(conn);
ServiceState serviceState = getServiceState();
- int rat = getRat(serviceState);
+ @NetworkType int rat = getRat(serviceState);
VoiceCallSession proto = new VoiceCallSession();
@@ -329,12 +396,13 @@
proto.disconnectExtraCode = conn.getPreciseDisconnectCause();
proto.disconnectExtraMessage = conn.getVendorDisconnectCause();
proto.ratAtStart = rat;
+ proto.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
proto.ratAtEnd = rat;
proto.ratSwitchCount = 0L;
proto.codecBitmask = 0L;
- proto.simSlotIndex = getSimSlotId();
- proto.isMultiSim = SimSlotState.getCurrentState().numActiveSims > 1;
- proto.isEsim = isEsim();
+ proto.simSlotIndex = mPhoneId;
+ proto.isMultiSim = SimSlotState.isMultiSim();
+ proto.isEsim = SimSlotState.isEsim(mPhoneId);
proto.carrierId = mPhone.getCarrierId();
proto.srvccCompleted = false;
proto.srvccFailureCount = 0L;
@@ -342,6 +410,7 @@
proto.rttEnabled = false;
proto.isEmergency = conn.isEmergencyCall();
proto.isRoaming = serviceState != null ? serviceState.getVoiceRoaming() : false;
+ proto.isMultiparty = conn.isMultiparty();
// internal fields for tracking
proto.setupBeginMillis = getTimeMillis();
@@ -364,6 +433,12 @@
mCallProtos.delete(connectionId);
proto.concurrentCallCountAtEnd = mCallProtos.size();
+ // Calculate signal strength at the end of the call
+ proto.signalStrengthAtEnd = getSignalStrength(proto.ratAtEnd);
+
+ // Calculate main codec quality
+ proto.mainCodecQuality = finalizeMainCodecQuality(connectionId);
+
// ensure internal fields are cleared
proto.setupBeginMillis = 0L;
@@ -421,17 +496,27 @@
private void checkCallSetup(Connection conn, VoiceCallSession proto) {
if (proto.setupBeginMillis != 0L && isSetupFinished(conn.getCall())) {
- proto.setupDuration = classifySetupDuration(getTimeMillis() - proto.setupBeginMillis);
+ proto.setupDurationMillis = (int) (getTimeMillis() - proto.setupBeginMillis);
+ proto.setupDuration = classifySetupDuration(proto.setupDurationMillis);
proto.setupBeginMillis = 0L;
}
- // clear setupFailed if call now active, but otherwise leave it unchanged
- if (conn.getState() == Call.State.ACTIVE) {
+ // Clear setupFailed if call now active, but otherwise leave it unchanged
+ // This block is executed only once, when call becomes active for the first time.
+ if (proto.setupFailed && conn.getState() == Call.State.ACTIVE) {
proto.setupFailed = false;
+ // Track RAT when voice call is connected.
+ ServiceState serviceState = getServiceState();
+ proto.ratAtConnected = getRat(serviceState);
+ // Reset list of codecs with the last codec at the present time. In this way, we
+ // track codec quality only after call is connected and not while ringing.
+ resetCodecList(conn);
}
}
private void updateRatTracker(ServiceState state) {
- int rat = getRat(state);
+ @NetworkType int rat = getRat(state);
+ int band = getBand(rat, state.getChannelNumber());
+
mRatUsage.add(mPhone.getCarrierId(), rat, getTimeMillis(), getConnectionIds());
for (int i = 0; i < mCallProtos.size(); i++) {
VoiceCallSession proto = mCallProtos.valueAt(i);
@@ -439,6 +524,7 @@
proto.ratSwitchCount++;
proto.ratAtEnd = rat;
}
+ proto.bandAtEnd = band;
// assuming that SIM carrier ID does not change during the call
}
}
@@ -452,23 +538,6 @@
finishCall(id);
}
- private boolean isEsim() {
- int slotId = getSimSlotId();
- UiccSlot slot = mUiccController.getUiccSlot(slotId);
- if (slot != null) {
- return slot.isEuicc();
- } else {
- // should not happen, but assume we are not using eSIM
- loge("isEsim: slot %d is null", slotId);
- return false;
- }
- }
-
- private int getSimSlotId() {
- // NOTE: UiccController's mapping hasn't be initialized when Phone was created
- return mUiccController.getSlotIdFromPhoneId(mPhoneId);
- }
-
private @Nullable ServiceState getServiceState() {
ServiceStateTracker tracker = mPhone.getServiceStateTracker();
return tracker != null ? tracker.getServiceState() : null;
@@ -505,8 +574,146 @@
return isWifiCall ? TelephonyManager.NETWORK_TYPE_IWLAN : state.getVoiceNetworkType();
}
- // NOTE: when setup is finished for MO calls, it is not successful yet.
+ /** Returns the band associated with a given rat and channel number. */
+ private int getBand(@NetworkType int rat, int chNumber) {
+ int band;
+ switch (rat) {
+ case TelephonyManager.NETWORK_TYPE_GSM:
+ case TelephonyManager.NETWORK_TYPE_GPRS:
+ case TelephonyManager.NETWORK_TYPE_EDGE:
+ band = AccessNetworkUtils.getOperatingBandForArfcn(chNumber);
+ break;
+ case TelephonyManager.NETWORK_TYPE_UMTS:
+ case TelephonyManager.NETWORK_TYPE_HSDPA:
+ case TelephonyManager.NETWORK_TYPE_HSUPA:
+ case TelephonyManager.NETWORK_TYPE_HSPA:
+ case TelephonyManager.NETWORK_TYPE_HSPAP:
+ band = AccessNetworkUtils.getOperatingBandForUarfcn(chNumber);
+ break;
+ case TelephonyManager.NETWORK_TYPE_LTE:
+ case TelephonyManager.NETWORK_TYPE_LTE_CA:
+ band = AccessNetworkUtils.getOperatingBandForEarfcn(chNumber);
+ break;
+ default:
+ band = 0;
+ break;
+ }
+ return band == AccessNetworkUtils.INVALID_BAND ? 0 : band;
+ }
+
+ /** Returns the signal strength. */
+ private int getSignalStrength(@NetworkType int rat) {
+ if (rat == TelephonyManager.NETWORK_TYPE_IWLAN) {
+ return getSignalStrengthWifi();
+ } else {
+ return getSignalStrengthCellular();
+ }
+ }
+
+ /** Returns the signal strength of WiFi. */
+ private int getSignalStrengthWifi() {
+ WifiManager wifiManager =
+ (WifiManager) mPhone.getContext().getSystemService(Context.WIFI_SERVICE);
+ WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+ int result = VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ if (wifiInfo != null) {
+ int level = wifiManager.calculateSignalLevel(wifiInfo.getRssi());
+ int max = wifiManager.getMaxSignalLevel();
+ // Scale result into 0 to 4 range.
+ result = VOICE_CALL_SESSION__SIGNAL_STRENGTH_AT_END__SIGNAL_STRENGTH_GREAT
+ * level / max;
+ logd(TAG, "WiFi level: " + result + " (" + level + "/" + max + ")");
+ }
+ return result;
+ }
+
+ /** Returns the signal strength of cellular RAT. */
+ private int getSignalStrengthCellular() {
+ return mPhone.getSignalStrength().getLevel();
+ }
+
+ /** Resets the list of codecs used for the connection with only the codec currently in use. */
+ private void resetCodecList(Connection conn) {
+ int id = getConnectionId(conn);
+ LongSparseArray<Integer> codecUsage = mCodecUsage.get(id);
+ if (codecUsage != null) {
+ int lastCodec = codecUsage.valueAt(codecUsage.size() - 1);
+ LongSparseArray<Integer> arr = new LongSparseArray<>();
+ arr.append(getTimeMillis(), lastCodec);
+ mCodecUsage.put(id, arr);
+ }
+ }
+
+ /** Returns the main codec quality used during the call. */
+ private int finalizeMainCodecQuality(int connectionId) {
+ // Retrieve information about codec usage for this call and remove it from main array.
+ if (!mCodecUsage.contains(connectionId)) {
+ return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
+ }
+ LongSparseArray<Integer> codecUsage = mCodecUsage.get(connectionId);
+ mCodecUsage.delete(connectionId);
+
+ // Append fake entry at the end, to facilitate the calculation of time for each codec.
+ codecUsage.put(getTimeMillis(), AudioCodec.AUDIO_CODEC_UNKNOWN);
+
+ // Calculate array with time for each quality
+ int totalTime = 0;
+ long[] timePerQuality = new long[CODEC_QUALITY_COUNT];
+ for (int i = 0; i < codecUsage.size() - 1; i++) {
+ long time = codecUsage.keyAt(i + 1) - codecUsage.keyAt(i);
+ int quality = getCodecQuality(codecUsage.valueAt(i));
+ timePerQuality[quality] += time;
+ totalTime += time;
+ }
+ logd(TAG, "Time per codec quality = " + Arrays.toString(timePerQuality));
+
+ // We calculate 70% duration of the call as the threshold for the main audio codec quality
+ // and iterate on all codec qualities. As soon as the sum of codec duration is greater than
+ // the threshold, we have identified the main codec quality.
+ long timeAtMinimumQuality = 0;
+ long timeThreshold = totalTime * MAIN_CODEC_QUALITY_THRESHOLD / 100;
+ for (int i = CODEC_QUALITY_COUNT - 1; i >= 0; i--) {
+ timeAtMinimumQuality += timePerQuality[i];
+ if (timeAtMinimumQuality >= timeThreshold) {
+ return i;
+ }
+ }
+ return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
+ }
+
+ private int getCodecQuality(int codec) {
+ switch(codec) {
+ case AudioCodec.AUDIO_CODEC_AMR:
+ case AudioCodec.AUDIO_CODEC_QCELP13K:
+ case AudioCodec.AUDIO_CODEC_EVRC:
+ case AudioCodec.AUDIO_CODEC_EVRC_B:
+ case AudioCodec.AUDIO_CODEC_EVRC_NW:
+ case AudioCodec.AUDIO_CODEC_GSM_EFR:
+ case AudioCodec.AUDIO_CODEC_GSM_FR:
+ case AudioCodec.AUDIO_CODEC_GSM_HR:
+ case AudioCodec.AUDIO_CODEC_G711U:
+ case AudioCodec.AUDIO_CODEC_G723:
+ case AudioCodec.AUDIO_CODEC_G711A:
+ case AudioCodec.AUDIO_CODEC_G722:
+ case AudioCodec.AUDIO_CODEC_G711AB:
+ case AudioCodec.AUDIO_CODEC_G729:
+ case AudioCodec.AUDIO_CODEC_EVS_NB:
+ return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ case AudioCodec.AUDIO_CODEC_AMR_WB:
+ case AudioCodec.AUDIO_CODEC_EVS_WB:
+ case AudioCodec.AUDIO_CODEC_EVRC_WB:
+ return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_WIDEBAND;
+ case AudioCodec.AUDIO_CODEC_EVS_SWB:
+ return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
+ case AudioCodec.AUDIO_CODEC_EVS_FB:
+ return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_FULLBAND;
+ default:
+ return VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
+ }
+ }
+
private static boolean isSetupFinished(@Nullable Call call) {
+ // NOTE: when setup is finished for MO calls, it is not successful yet.
if (call != null) {
switch (call.getState()) {
case ACTIVE: // MT setup: accepted to ACTIVE
@@ -518,19 +725,19 @@
return false;
}
- private static long audioQualityToCodecBitmask(int bearer, int audioQuality) {
+ private static int audioQualityToCodec(int bearer, int audioQuality) {
switch (bearer) {
case VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS:
- return CS_CODEC_MAP.get(audioQuality, AUDIO_CODEC_UNKNOWN);
+ return CS_CODEC_MAP.get(audioQuality, AudioCodec.AUDIO_CODEC_UNKNOWN);
case VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS:
- return IMS_CODEC_MAP.get(audioQuality, AUDIO_CODEC_UNKNOWN);
+ return IMS_CODEC_MAP.get(audioQuality, AudioCodec.AUDIO_CODEC_UNKNOWN);
default:
- loge("audioQualityToCodecBitmask: unknown bearer %d", bearer);
- return AUDIO_CODEC_UNKNOWN;
+ loge("audioQualityToCodec: unknown bearer %d", bearer);
+ return AudioCodec.AUDIO_CODEC_UNKNOWN;
}
}
- private static int classifySetupDuration(long durationMillis) {
+ private static int classifySetupDuration(int durationMillis) {
// keys in CALL_SETUP_DURATION_MAP are upper bounds in ascending order
for (int i = 0; i < CALL_SETUP_DURATION_MAP.size(); i++) {
if (durationMillis < CALL_SETUP_DURATION_MAP.keyAt(i)) {
@@ -566,48 +773,42 @@
Rlog.e(TAG, String.format(format, args));
}
- private static SparseLongArray buildGsmCdmaCodecMap() {
- SparseLongArray map = new SparseLongArray();
-
- map.put(DriverCall.AUDIO_QUALITY_AMR, 1L << AudioCodec.AUDIO_CODEC_AMR);
- map.put(DriverCall.AUDIO_QUALITY_AMR_WB, 1L << AudioCodec.AUDIO_CODEC_AMR_WB);
- map.put(DriverCall.AUDIO_QUALITY_GSM_EFR, 1L << AudioCodec.AUDIO_CODEC_GSM_EFR);
- map.put(DriverCall.AUDIO_QUALITY_GSM_FR, 1L << AudioCodec.AUDIO_CODEC_GSM_FR);
- map.put(DriverCall.AUDIO_QUALITY_GSM_HR, 1L << AudioCodec.AUDIO_CODEC_GSM_HR);
- map.put(DriverCall.AUDIO_QUALITY_EVRC, 1L << AudioCodec.AUDIO_CODEC_EVRC);
- map.put(DriverCall.AUDIO_QUALITY_EVRC_B, 1L << AudioCodec.AUDIO_CODEC_EVRC_B);
- map.put(DriverCall.AUDIO_QUALITY_EVRC_WB, 1L << AudioCodec.AUDIO_CODEC_EVRC_WB);
- map.put(DriverCall.AUDIO_QUALITY_EVRC_NW, 1L << AudioCodec.AUDIO_CODEC_EVRC_NW);
-
+ private static SparseIntArray buildGsmCdmaCodecMap() {
+ SparseIntArray map = new SparseIntArray();
+ map.put(DriverCall.AUDIO_QUALITY_AMR, AudioCodec.AUDIO_CODEC_AMR);
+ map.put(DriverCall.AUDIO_QUALITY_AMR_WB, AudioCodec.AUDIO_CODEC_AMR_WB);
+ map.put(DriverCall.AUDIO_QUALITY_GSM_EFR, AudioCodec.AUDIO_CODEC_GSM_EFR);
+ map.put(DriverCall.AUDIO_QUALITY_GSM_FR, AudioCodec.AUDIO_CODEC_GSM_FR);
+ map.put(DriverCall.AUDIO_QUALITY_GSM_HR, AudioCodec.AUDIO_CODEC_GSM_HR);
+ map.put(DriverCall.AUDIO_QUALITY_EVRC, AudioCodec.AUDIO_CODEC_EVRC);
+ map.put(DriverCall.AUDIO_QUALITY_EVRC_B, AudioCodec.AUDIO_CODEC_EVRC_B);
+ map.put(DriverCall.AUDIO_QUALITY_EVRC_WB, AudioCodec.AUDIO_CODEC_EVRC_WB);
+ map.put(DriverCall.AUDIO_QUALITY_EVRC_NW, AudioCodec.AUDIO_CODEC_EVRC_NW);
return map;
}
- private static SparseLongArray buildImsCodecMap() {
- SparseLongArray map = new SparseLongArray();
-
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_AMR, 1L << AudioCodec.AUDIO_CODEC_AMR);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB, 1L << AudioCodec.AUDIO_CODEC_AMR_WB);
- map.put(
- ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K,
- 1L << AudioCodec.AUDIO_CODEC_QCELP13K);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC, 1L << AudioCodec.AUDIO_CODEC_EVRC);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B, 1L << AudioCodec.AUDIO_CODEC_EVRC_B);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB, 1L << AudioCodec.AUDIO_CODEC_EVRC_WB);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW, 1L << AudioCodec.AUDIO_CODEC_EVRC_NW);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR, 1L << AudioCodec.AUDIO_CODEC_GSM_EFR);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR, 1L << AudioCodec.AUDIO_CODEC_GSM_FR);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR, 1L << AudioCodec.AUDIO_CODEC_GSM_HR);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711U, 1L << AudioCodec.AUDIO_CODEC_G711U);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G723, 1L << AudioCodec.AUDIO_CODEC_G723);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711A, 1L << AudioCodec.AUDIO_CODEC_G711A);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G722, 1L << AudioCodec.AUDIO_CODEC_G722);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711AB, 1L << AudioCodec.AUDIO_CODEC_G711AB);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G729, 1L << AudioCodec.AUDIO_CODEC_G729);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB, 1L << AudioCodec.AUDIO_CODEC_EVS_NB);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB, 1L << AudioCodec.AUDIO_CODEC_EVS_WB);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB, 1L << AudioCodec.AUDIO_CODEC_EVS_SWB);
- map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB, 1L << AudioCodec.AUDIO_CODEC_EVS_FB);
-
+ private static SparseIntArray buildImsCodecMap() {
+ SparseIntArray map = new SparseIntArray();
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_AMR, AudioCodec.AUDIO_CODEC_AMR);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB, AudioCodec.AUDIO_CODEC_AMR_WB);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K, AudioCodec.AUDIO_CODEC_QCELP13K);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC, AudioCodec.AUDIO_CODEC_EVRC);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B, AudioCodec.AUDIO_CODEC_EVRC_B);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB, AudioCodec.AUDIO_CODEC_EVRC_WB);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW, AudioCodec.AUDIO_CODEC_EVRC_NW);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR, AudioCodec.AUDIO_CODEC_GSM_EFR);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR, AudioCodec.AUDIO_CODEC_GSM_FR);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR, AudioCodec.AUDIO_CODEC_GSM_HR);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711U, AudioCodec.AUDIO_CODEC_G711U);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G723, AudioCodec.AUDIO_CODEC_G723);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711A, AudioCodec.AUDIO_CODEC_G711A);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G722, AudioCodec.AUDIO_CODEC_G722);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G711AB, AudioCodec.AUDIO_CODEC_G711AB);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_G729, AudioCodec.AUDIO_CODEC_G729);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB, AudioCodec.AUDIO_CODEC_EVS_NB);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB, AudioCodec.AUDIO_CODEC_EVS_WB);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB, AudioCodec.AUDIO_CODEC_EVS_SWB);
+ map.put(ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB, AudioCodec.AUDIO_CODEC_EVS_FB);
return map;
}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java b/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
index 3513bb9..1254067 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
@@ -53,7 +53,7 @@
private class FileHandler extends Handler {
// EF path for PKCS15 root, eg. "3F007F50"
// null if logical channel is used for PKCS15 access.
- private final String mPkcs15Path;
+ final String mPkcs15Path;
// Message to send when file has been parsed.
private Message mCallback;
// File id to read data from, eg. "5031"
@@ -90,7 +90,7 @@
private void readBinary() {
if (mChannelId >=0 ) {
mUiccProfile.iccTransmitApduLogicalChannel(mChannelId, 0x00, 0xB0, 0x00, 0x00, 0x00,
- "", obtainMessage(EVENT_READ_BINARY_DONE));
+ mFileId, obtainMessage(EVENT_READ_BINARY_DONE));
} else {
log("EF based");
}
@@ -200,8 +200,8 @@
// ar.result is null if using logical channel,
// or string for pkcs15 path if using file access.
mFh = new FileHandler((String) ar.result);
- if (!mFh.loadFile(ID_ACRF, obtainMessage(EVENT_LOAD_ACRF_DONE))) {
- cleanUp();
+ if (!mFh.loadFile(EFODF_PATH, obtainMessage(EVENT_LOAD_ODF_DONE))) {
+ startFromAcrf();
}
} else {
log("select pkcs15 failed: " + ar.exception);
@@ -210,6 +210,39 @@
}
break;
+ case EVENT_LOAD_ODF_DONE:
+ if (ar.exception == null && ar.result != null) {
+ String idDodf = parseOdf((String) ar.result);
+ if (!mFh.loadFile(idDodf, obtainMessage(EVENT_LOAD_DODF_DONE))) {
+ startFromAcrf();
+ }
+ } else {
+ startFromAcrf();
+ }
+ break;
+
+ case EVENT_LOAD_DODF_DONE:
+ if (ar.exception == null && ar.result != null) {
+ String idAcmf = parseDodf((String) ar.result);
+ if (!mFh.loadFile(idAcmf, obtainMessage(EVENT_LOAD_ACMF_DONE))) {
+ startFromAcrf();
+ }
+ } else {
+ startFromAcrf();
+ }
+ break;
+
+ case EVENT_LOAD_ACMF_DONE:
+ if (ar.exception == null && ar.result != null) {
+ String idAcrf = parseAcmf((String) ar.result);
+ if (!mFh.loadFile(idAcrf, obtainMessage(EVENT_LOAD_ACRF_DONE))) {
+ startFromAcrf();
+ }
+ } else {
+ startFromAcrf();
+ }
+ break;
+
case EVENT_LOAD_ACRF_DONE:
if (ar.exception == null && ar.result != null) {
mRules = new ArrayList<String>();
@@ -238,6 +271,13 @@
}
}
+ private void startFromAcrf() {
+ log("Fallback to use ACRF_PATH");
+ if (!mFh.loadFile(ACRF_PATH, obtainMessage(EVENT_LOAD_ACRF_DONE))) {
+ cleanUp();
+ }
+ }
+
private void cleanUp() {
log("cleanUp");
if (mChannelId >= 0) {
@@ -250,10 +290,125 @@
// Constants defined in specs, needed for parsing
private static final String CARRIER_RULE_AID = "FFFFFFFFFFFF"; // AID for carrier privilege rule
- private static final String ID_ACRF = "4300";
+ private static final String ACRF_PATH = "4300";
+ private static final String EFODF_PATH = "5031";
private static final String TAG_ASN_SEQUENCE = "30";
private static final String TAG_ASN_OCTET_STRING = "04";
+ private static final String TAG_ASN_OID = "06";
private static final String TAG_TARGET_AID = "A0";
+ private static final String TAG_ODF = "A7";
+ private static final String TAG_DODF = "A1";
+ private static final String REFRESH_TAG_LEN = "08";
+ // OID defined by Global Platform for the "Access Control". The hexstring here can be converted
+ // to OID string value 1.2.840.114283.200.1.1
+ public static final String AC_OID = "060A2A864886FC6B81480101";
+
+
+ // parse ODF file to get file id for DODF file
+ // data is hex string, return file id if parse success, null otherwise
+ private String parseOdf(String data) {
+ // Example:
+ // [A7] 06 [30] 04 [04] 02 52 07
+ try {
+ TLV tlvRule = new TLV(TAG_ODF); // A7
+ tlvRule.parse(data, false);
+ String ruleString = tlvRule.getValue();
+ TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30
+ TLV tlvPath = new TLV(TAG_ASN_OCTET_STRING); // 04
+ tlvAsnPath.parse(ruleString, true);
+ tlvPath.parse(tlvAsnPath.getValue(), true);
+ return tlvPath.getValue();
+ } catch (IllegalArgumentException | IndexOutOfBoundsException ex) {
+ log("Error: " + ex);
+ return null;
+ }
+ }
+
+ // parse DODF file to get file id for ACMF file
+ // data is hex string, return file id if parse success, null otherwise
+ private String parseDodf(String data) {
+ // Example:
+ // [A1] 29 [30] 00 [30] 0F 0C 0D 47 50 20 53 45 20 41 63 63 20 43 74 6C [A1] 14 [30] 12
+ // [06] 0A 2A 86 48 86 FC 6B 81 48 01 01 [30] 04 04 02 42 00
+ String ret = null;
+ String acRules = data;
+ while (!acRules.isEmpty()) {
+ TLV dodfTag = new TLV(TAG_DODF); // A1
+ try {
+ acRules = dodfTag.parse(acRules, false);
+ String ruleString = dodfTag.getValue();
+ // Skip the Common Object Attributes
+ TLV commonObjectAttributes = new TLV(TAG_ASN_SEQUENCE); // 30
+ ruleString = commonObjectAttributes.parse(ruleString, false);
+
+ // Skip the Common Data Object Attributes
+ TLV commonDataObjectAttributes = new TLV(TAG_ASN_SEQUENCE); // 30
+ ruleString = commonDataObjectAttributes.parse(ruleString, false);
+
+ if (ruleString.startsWith(TAG_TARGET_AID)) {
+ // Skip SubClassAttributes [Optional]
+ TLV subClassAttributes = new TLV(TAG_TARGET_AID); // A0
+ ruleString = subClassAttributes.parse(ruleString, false);
+ }
+
+ if (ruleString.startsWith(TAG_DODF)) {
+ TLV oidDoTag = new TLV(TAG_DODF); // A1
+ oidDoTag.parse(ruleString, true);
+ ruleString = oidDoTag.getValue();
+
+ TLV oidDo = new TLV(TAG_ASN_SEQUENCE); // 30
+ oidDo.parse(ruleString, true);
+ ruleString = oidDo.getValue();
+
+ TLV oidTag = new TLV(TAG_ASN_OID); // 06
+ oidTag.parse(ruleString, false);
+ // Example : [06] 0A 2A 86 48 86 FC 6B 81 48 01 01
+ String oid = oidTag.getValue();
+ if (oid.equals(AC_OID)) {
+ // Skip OID and get the AC to the ACCM
+ ruleString = oidTag.parse(ruleString, false);
+ TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30
+ TLV tlvPath = new TLV(TAG_ASN_OCTET_STRING); // 04
+ tlvAsnPath.parse(ruleString, true);
+ tlvPath.parse(tlvAsnPath.getValue(), true);
+ return tlvPath.getValue();
+ }
+ }
+ continue; // skip current rule as it doesn't have expected TAG
+ } catch (IllegalArgumentException | IndexOutOfBoundsException ex) {
+ log("Error: " + ex);
+ break; // Bad data, ignore all remaining ACRules
+ }
+ }
+ return ret;
+ }
+
+ // parse ACMF file to get file id for ACRF file
+ // data is hex string, return file id if parse success, null otherwise
+ private String parseAcmf(String data) {
+ try {
+ // [30] 10 [04] 08 01 02 03 04 05 06 07 08 [30] 04 [04] 02 43 00
+ TLV acmfTag = new TLV(TAG_ASN_SEQUENCE); // 30
+ acmfTag.parse(data, false);
+ String ruleString = acmfTag.getValue();
+ TLV refreshTag = new TLV(TAG_ASN_OCTET_STRING); // 04
+ String refreshTagLength = refreshTag.parseLength(ruleString);
+ if (!refreshTagLength.equals(REFRESH_TAG_LEN)) {
+ log("Error: refresh tag in ACMF must be 8.");
+ return null;
+ }
+ ruleString = refreshTag.parse(ruleString, false);
+ TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30
+ TLV tlvPath = new TLV(TAG_ASN_OCTET_STRING); // 04
+ tlvAsnPath.parse(ruleString, true);
+ tlvPath.parse(tlvAsnPath.getValue(), true);
+ return tlvPath.getValue();
+ } catch (IllegalArgumentException | IndexOutOfBoundsException ex) {
+ log("Error: " + ex);
+ return null;
+ }
+
+ }
// parse ACRF file to get file id for ACCF file
// data is hex string, return file id if parse success, null otherwise
@@ -262,14 +417,14 @@
String acRules = data;
while (!acRules.isEmpty()) {
+ // Example:
+ // [30] 10 [A0] 08 04 06 FF FF FF FF FF FF [30] 04 [04] 02 43 10
+ // bytes in [] are tags for the data
TLV tlvRule = new TLV(TAG_ASN_SEQUENCE);
try {
acRules = tlvRule.parse(acRules, false);
String ruleString = tlvRule.getValue();
if (ruleString.startsWith(TAG_TARGET_AID)) {
- // rule string consists of target AID + path, example:
- // [A0] 08 [04] 06 FF FF FF FF FF FF [30] 04 [04] 02 43 10
- // bytes in [] are tags for the data
TLV tlvTarget = new TLV(TAG_TARGET_AID); // A0
TLV tlvAid = new TLV(TAG_ASN_OCTET_STRING); // 04
TLV tlvAsnPath = new TLV(TAG_ASN_SEQUENCE); // 30
diff --git a/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDcTracker.java b/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDcTracker.java
index 315a13c..d219df9 100644
--- a/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDcTracker.java
+++ b/src/java/com/android/internal/telephony/vendor/dataconnection/VendorDcTracker.java
@@ -148,7 +148,7 @@
@Override
protected void onDataSetupCompleteError(ApnContext apnContext,
- @RequestNetworkType int requestType, boolean fallback) {
+ @RequestNetworkType int requestType, boolean fallbackOnFailedHandover) {
long delay = apnContext.getDelayForNextApn(mFailFast);
if (mPhone.getContext().getResources().getBoolean(
com.android.internal.R.bool.config_pdp_reject_enable_retry)) {
@@ -275,7 +275,8 @@
cancelReconnect(apnContext);
if (retry) {
if (DBG) log("onResetEvent: retry data call on apnContext=" + apnContext);
- sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, apnContext));
+ sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA,
+ REQUEST_TYPE_NORMAL, 0, apnContext));
}
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java
index cd97bdc..7bc26b7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java
@@ -51,7 +51,8 @@
mInboundSmsTracker = new InboundSmsTracker(InstrumentationRegistry.getContext(),
FAKE_PDU, FAKE_TIMESTAMP, FAKE_DEST_PORT, false,
FAKE_ADDRESS, FAKE_DISPLAY_ADDRESS, FAKE_REFERENCE_NUMBER, FAKE_SEQUENCE_NUMBER,
- FAKE_MESSAGE_COUNT, false, FAKE_MESSAGE_BODY, false /* isClass0 */, FAKE_SUBID);
+ FAKE_MESSAGE_COUNT, false, FAKE_MESSAGE_BODY, false /* isClass0 */, FAKE_SUBID,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
}
public static MatrixCursor createFakeCursor() {
@@ -87,6 +88,7 @@
assertEquals(FAKE_DISPLAY_ADDRESS, mInboundSmsTracker.getDisplayAddress());
assertEquals(false, mInboundSmsTracker.isClass0());
assertEquals(FAKE_SUBID, mInboundSmsTracker.getSubId());
+ assertEquals(InboundSmsHandler.SOURCE_NOT_INJECTED, mInboundSmsTracker.getSource());
// assertNotEquals(0L, mInboundSmsTracker.getMessageId());
String[] args = new String[]{"123"};
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index 3098a8c..583eb82 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -98,6 +98,7 @@
import com.android.internal.R;
import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
+import com.android.internal.telephony.metrics.ServiceStateStats;
import com.android.internal.telephony.test.SimulatedCommands;
import com.android.internal.telephony.uicc.IccCardApplicationStatus;
import com.android.internal.telephony.uicc.IccRecords;
@@ -136,6 +137,9 @@
@Mock
private SubscriptionInfo mSubInfo;
+ @Mock
+ private ServiceStateStats mServiceStateStats;
+
private ServiceStateTracker sst;
private ServiceStateTrackerTestHandler mSSTTestHandler;
private PersistableBundle mBundle;
@@ -185,6 +189,7 @@
@Override
public void onLooperPrepared() {
sst = new ServiceStateTracker(mPhone, mSimulatedCommands);
+ sst.setServiceStateStats(mServiceStateStats);
setReady(true);
}
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java
index ddf5bcd..0f0a031 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SignalThresholdInfoTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
+import android.telephony.AccessNetworkConstants;
import android.telephony.SignalThresholdInfo;
import androidx.test.filters.SmallTest;
@@ -33,32 +34,94 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
@RunWith(JUnit4.class)
public class SignalThresholdInfoTest extends TestCase {
private static final int HYSTERESIS_DB = 2;
private static final int HYSTERESIS_MS = 30;
- private static final int[] SSRSRP_THRESHOLDS = new int[] {-30, 10, 45, 130};
+ private static final int[] SSRSRP_THRESHOLDS = new int[]{-120, -100, -80, -60};
- private final int[] mRssiThresholds = new int[] {-109, -103, -97, -89};
- private final int[] mRscpThresholds = new int[] {-115, -105, -95, -85};
- private final int[] mRsrpThresholds = new int[] {-128, -118, -108, -98};
- private final int[] mRsrqThresholds = new int[] {-19, -17, -14, -12};
- private final int[] mRssnrThresholds = new int[] {-30, 10, 45, 130};
- private final int[][] mThresholds = new int[5][];
+ // Map of SignalMeasurementType to invalid thresholds edge values.
+ // Each invalid value will be constructed with a thresholds array to test separately.
+ private static final Map<Integer, List<Integer>> INVALID_THRESHOLDS_MAP = Map.of(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI,
+ List.of(SignalThresholdInfo.SIGNAL_RSSI_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSSI_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP,
+ List.of(SignalThresholdInfo.SIGNAL_RSCP_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSCP_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
+ List.of(SignalThresholdInfo.SIGNAL_RSRP_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSRP_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
+ List.of(SignalThresholdInfo.SIGNAL_RSRQ_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSRQ_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR,
+ List.of(SignalThresholdInfo.SIGNAL_RSSNR_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_RSSNR_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+ List.of(SignalThresholdInfo.SIGNAL_SSRSRP_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_SSRSRP_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
+ List.of(SignalThresholdInfo.SIGNAL_SSRSRQ_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_SSRSRQ_MAX_VALUE + 1),
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR,
+ List.of(SignalThresholdInfo.SIGNAL_SSSINR_MIN_VALUE - 1,
+ SignalThresholdInfo.SIGNAL_SSSINR_MAX_VALUE + 1)
+ );
+
+ // Map of RAN to allowed SignalMeasurementType set.
+ // RAN/TYPE pair will be used to verify the validation of the combo
+ private static final Map<Integer, Set<Integer>> VALID_RAN_TO_MEASUREMENT_TYPE_MAP = Map.of(
+ AccessNetworkConstants.AccessNetworkType.GERAN,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI),
+ AccessNetworkConstants.AccessNetworkType.CDMA2000,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI),
+ AccessNetworkConstants.AccessNetworkType.UTRAN,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP),
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR),
+ AccessNetworkConstants.AccessNetworkType.NGRAN,
+ Set.of(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
+ );
+
+ // Deliberately picking up the max/min value in each range to test the edge cases
+ private final int[] mRssiThresholds = new int[]{-113, -103, -97, -51};
+ private final int[] mRscpThresholds = new int[]{-120, -105, -95, -25};
+ private final int[] mRsrpThresholds = new int[]{-140, -118, -108, -44};
+ private final int[] mRsrqThresholds = new int[]{-34, -17, -14, 3};
+ private final int[] mRssnrThresholds = new int[]{-20, 10, 20, 30};
+ private final int[] mSsrsrpThresholds = new int[]{-140, -118, -98, -44};
+ private final int[] mSsrsrqThresholds = new int[]{-43, -17, -14, 20};
+ private final int[] mSssinrThresholds = new int[]{-23, -16, -10, 40};
+
+ private final int[][] mThresholds = {mRssiThresholds, mRscpThresholds, mRsrpThresholds,
+ mRsrqThresholds, mRssnrThresholds, mSsrsrpThresholds, mSsrsrqThresholds,
+ mSssinrThresholds};
@Test
@SmallTest
public void testSignalThresholdInfo() throws Exception {
- SignalThresholdInfo signalThresholdInfo = new SignalThresholdInfo(
- SignalThresholdInfo.SIGNAL_SSRSRP,
- HYSTERESIS_MS,
- HYSTERESIS_DB,
- SSRSRP_THRESHOLDS,
- false);
+ SignalThresholdInfo signalThresholdInfo =
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(SSRSRP_THRESHOLDS)
+ .setIsEnabled(false)
+ .build();
- assertEquals(SignalThresholdInfo.SIGNAL_SSRSRP,
- signalThresholdInfo.getSignalMeasurement());
+ assertEquals(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+ signalThresholdInfo.getSignalMeasurementType());
assertEquals(HYSTERESIS_MS, signalThresholdInfo.getHysteresisMs());
assertEquals(HYSTERESIS_DB, signalThresholdInfo.getHysteresisDb());
assertEquals(Arrays.toString(SSRSRP_THRESHOLDS), Arrays.toString(
@@ -68,9 +131,8 @@
@Test
@SmallTest
- public void testDefaultThresholdsConstruction() {
- setThresholds();
- ArrayList<SignalThresholdInfo> stList = setSignalThresholdInfoConstructor();
+ public void testBuilderWithAllFields() {
+ ArrayList<SignalThresholdInfo> stList = buildSignalThresholdInfoWithAllFields();
int count = 0;
for (SignalThresholdInfo st : stList) {
@@ -82,7 +144,7 @@
@Test
@SmallTest
public void testDefaultThresholdsParcel() {
- ArrayList<SignalThresholdInfo> stList = setSignalThresholdInfoConstructor();
+ ArrayList<SignalThresholdInfo> stList = buildSignalThresholdInfoWithAllFields();
for (SignalThresholdInfo st : stList) {
Parcel p = Parcel.obtain();
@@ -98,58 +160,265 @@
@SmallTest
public void testGetSignalThresholdInfo() {
ArrayList<SignalThresholdInfo> stList = new ArrayList<>();
- stList.add(new SignalThresholdInfo(0, 0, 0, null, false));
- stList.add(new SignalThresholdInfo(SignalThresholdInfo.SIGNAL_RSSI, HYSTERESIS_MS,
- HYSTERESIS_DB, mRssiThresholds, false));
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setHysteresisMs(0)
+ .setHysteresisDb(0)
+ .setThresholds(new int[]{})
+ .setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setHysteresisMs(HYSTERESIS_MS).setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRssiThresholds)
+ .setIsEnabled(false)
+ .build());
- assertThat(stList.get(0).getThresholds()).isEqualTo(null);
- assertThat(stList.get(1).getSignalMeasurement()).isEqualTo(SignalThresholdInfo.SIGNAL_RSSI);
+ assertThat(stList.get(0).getThresholds()).isEqualTo(new int[]{});
+ assertThat(stList.get(1).getSignalMeasurementType()).isEqualTo(
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI);
assertThat(stList.get(1).getThresholds()).isEqualTo(mRssiThresholds);
}
@Test
@SmallTest
public void testEqualsSignalThresholdInfo() {
- final int[] dummyThresholds = new int[] {-100, -1, 1, 100};
- SignalThresholdInfo st1 = new SignalThresholdInfo(1, HYSTERESIS_MS, HYSTERESIS_DB,
- mRssiThresholds, false);
- SignalThresholdInfo st2 = new SignalThresholdInfo(2, HYSTERESIS_MS, HYSTERESIS_DB,
- mRssiThresholds, false);
- SignalThresholdInfo st3 = new SignalThresholdInfo(1, HYSTERESIS_MS, HYSTERESIS_DB,
- dummyThresholds, false);
- SignalThresholdInfo st4 = new SignalThresholdInfo(1, HYSTERESIS_MS, HYSTERESIS_DB,
- mRssiThresholds, false);
+ final int[] dummyThresholds = new int[]{-100, -90, -70, -60};
+ final int[] dummyThreholdsDisordered = new int[]{-60, -90, -100, -70};
+ SignalThresholdInfo st1 = new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(1).setSignalMeasurementType(1)
+ .setHysteresisMs(HYSTERESIS_MS).setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRssiThresholds).setIsEnabled(false)
+ .build();
+ SignalThresholdInfo st2 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(2)
+ .setSignalMeasurementType(2).setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB).setThresholds(mRssiThresholds).setIsEnabled(false)
+ .build();
+ SignalThresholdInfo st3 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(1)
+ .setSignalMeasurementType(1).setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB).setThresholds(dummyThresholds).setIsEnabled(false)
+ .build();
+ SignalThresholdInfo st4 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(1)
+ .setSignalMeasurementType(1).setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB).setThresholds(mRssiThresholds).setIsEnabled(false)
+ .build();
+ SignalThresholdInfo st5 = new SignalThresholdInfo.Builder().setRadioAccessNetworkType(1)
+ .setSignalMeasurementType(1).setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB).setThresholds(dummyThreholdsDisordered)
+ .setIsEnabled(false).build();
//Return true if all SignalThresholdInfo values match.
assertTrue(st1.equals(st1));
assertFalse(st1.equals(st2));
assertFalse(st1.equals(st3));
assertTrue(st1.equals(st4));
+ //Threshold values ordering doesn't matter
+ assertTrue(st3.equals(st5));
//Return false if the object of argument is other than SignalThresholdInfo.
assertFalse(st1.equals(new String("test")));
}
- private void setThresholds() {
- mThresholds[0] = mRssiThresholds;
- mThresholds[1] = mRscpThresholds;
- mThresholds[2] = mRsrpThresholds;
- mThresholds[3] = mRsrqThresholds;
- mThresholds[4] = mRssnrThresholds;
+ @Test
+ @SmallTest
+ public void testBuilderWithValidParameters() {
+ ArrayList<SignalThresholdInfo> stList = buildSignalThresholdInfoWithPublicFields();
+
+ for (int i = 0; i < stList.size(); i++) {
+ SignalThresholdInfo st = stList.get(i);
+ assertThat(st.getThresholds()).isEqualTo(mThresholds[i]);
+ assertThat(st.getHysteresisMs()).isEqualTo(SignalThresholdInfo.HYSTERESIS_MS_DISABLED);
+ assertThat(st.getHysteresisDb()).isEqualTo(SignalThresholdInfo.HYSTERESIS_DB_DISABLED);
+ assertFalse(st.isEnabled());
+ }
}
- private ArrayList<SignalThresholdInfo> setSignalThresholdInfoConstructor() {
+ @Test
+ @SmallTest
+ public void testBuilderWithInvalidParameter() {
+ // Invalid signal measurement type
+ int[] invalidSignalMeasurementTypes = new int[]{-1, 0, 9};
+ for (int signalMeasurementType : invalidSignalMeasurementTypes) {
+ buildWithInvalidParameterThrowException(
+ AccessNetworkConstants.AccessNetworkType.GERAN, signalMeasurementType,
+ new int[]{-1});
+ }
+
+ // Null thresholds array
+ buildWithInvalidParameterThrowException(AccessNetworkConstants.AccessNetworkType.GERAN,
+ SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI, null);
+
+ // Thresholds value out of range
+ for (int signalMeasurementType : INVALID_THRESHOLDS_MAP.keySet()) {
+ List<Integer> invalidThresholds = INVALID_THRESHOLDS_MAP.get(signalMeasurementType);
+ for (int threshold : invalidThresholds) {
+ buildWithInvalidParameterThrowException(getValidRan(signalMeasurementType),
+ signalMeasurementType, new int[]{threshold});
+ }
+ }
+
+ // Invalid RAN/Measurement type combos
+ for (int ran : VALID_RAN_TO_MEASUREMENT_TYPE_MAP.keySet()) {
+ Set validTypes = VALID_RAN_TO_MEASUREMENT_TYPE_MAP.get(ran);
+ for (int type = SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI;
+ type <= SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR; type++) {
+ if (!validTypes.contains(type)) {
+ buildWithInvalidParameterThrowException(ran, type, new int[]{-1});
+ }
+ }
+ }
+ }
+
+ private void buildWithInvalidParameterThrowException(int ran, int signalMeasurementType,
+ int[] thresholds) {
+ try {
+ new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(ran)
+ .setSignalMeasurementType(signalMeasurementType)
+ .setThresholds(thresholds)
+ .build();
+ fail("exception expected");
+ } catch (IllegalArgumentException | NullPointerException expected) {
+ }
+ }
+
+ private ArrayList<SignalThresholdInfo> buildSignalThresholdInfoWithAllFields() {
ArrayList<SignalThresholdInfo> stList = new ArrayList<>();
- stList.add(new SignalThresholdInfo(SignalThresholdInfo.SIGNAL_RSSI, HYSTERESIS_MS,
- HYSTERESIS_DB, mRssiThresholds, false));
- stList.add(new SignalThresholdInfo(SignalThresholdInfo.SIGNAL_RSCP, HYSTERESIS_MS,
- HYSTERESIS_DB, mRscpThresholds, false));
- stList.add(new SignalThresholdInfo(SignalThresholdInfo.SIGNAL_RSRP, HYSTERESIS_MS,
- HYSTERESIS_DB, mRsrpThresholds, false));
- stList.add(new SignalThresholdInfo(SignalThresholdInfo.SIGNAL_RSRQ, HYSTERESIS_MS,
- HYSTERESIS_DB, mRsrqThresholds, false));
- stList.add(new SignalThresholdInfo(SignalThresholdInfo.SIGNAL_RSSNR, HYSTERESIS_MS,
- HYSTERESIS_DB, mRssnrThresholds, false));
+
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setHysteresisMs(HYSTERESIS_MS).setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRssiThresholds).setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRscpThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRsrpThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRsrqThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mRssnrThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mSsrsrpThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mSsrsrqThresholds)
+ .setIsEnabled(false)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
+ .setHysteresisMs(HYSTERESIS_MS)
+ .setHysteresisDb(HYSTERESIS_DB)
+ .setThresholds(mSssinrThresholds)
+ .setIsEnabled(false)
+ .build());
return stList;
}
+
+ private ArrayList<SignalThresholdInfo> buildSignalThresholdInfoWithPublicFields() {
+ ArrayList<SignalThresholdInfo> stList = new ArrayList<>();
+
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.GERAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI)
+ .setThresholds(mRssiThresholds)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.UTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP)
+ .setThresholds(mRscpThresholds)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP)
+ .setThresholds(mRsrpThresholds)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ)
+ .setThresholds(mRsrqThresholds)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR)
+ .setThresholds(mRssnrThresholds)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP)
+ .setThresholds(mSsrsrpThresholds)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ)
+ .setThresholds(mSsrsrqThresholds)
+ .build());
+ stList.add(new SignalThresholdInfo.Builder()
+ .setRadioAccessNetworkType(AccessNetworkConstants.AccessNetworkType.NGRAN)
+ .setSignalMeasurementType(SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR)
+ .setThresholds(mSssinrThresholds)
+ .build());
+
+ return stList;
+ }
+
+ /**
+ * Return a possible valid RAN value for the measurement type. This is used to prevent the
+ * invalid ran/type causing IllegalArgumentException when testing other invalid input cases.
+ */
+ private static int getValidRan(@SignalThresholdInfo.SignalMeasurementType int type) {
+ switch (type) {
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSI:
+ return AccessNetworkConstants.AccessNetworkType.GERAN;
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSCP:
+ return AccessNetworkConstants.AccessNetworkType.UTRAN;
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRP:
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSRQ:
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_RSSNR:
+ return AccessNetworkConstants.AccessNetworkType.EUTRAN;
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRP:
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSRSRQ:
+ case SignalThresholdInfo.SIGNAL_MEASUREMENT_TYPE_SSSINR:
+ return AccessNetworkConstants.AccessNetworkType.NGRAN;
+ default:
+ return AccessNetworkConstants.AccessNetworkType.UNKNOWN;
+ }
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SipMessageParsingUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/SipMessageParsingUtilsTest.java
new file mode 100644
index 0000000..50ca36a
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/SipMessageParsingUtilsTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+
+import android.telephony.ims.SipMessage;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test SIP Message parsing utilities in frameworks/base. see {@link SipMessageParsingUtils}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class SipMessageParsingUtilsTest {
+
+ // Sample messages from RFC 3261 modified for parsing use cases.
+ public static final SipMessage SIP_MESSAGE_1 = new SipMessage(
+ "INVITE sip:bob@biloxi.com SIP/2.0",
+ // Typical Via
+ "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\n"
+ + "Max-Forwards: 70\n"
+ + "To: Bob <sip:bob@biloxi.com>\n"
+ + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+ + "Call-ID: a84b4c76e66710@pc33.atlanta.com\n"
+ + "CSeq: 314159 INVITE\n"
+ + "Contact: <sip:alice@pc33.atlanta.com>\n"
+ + "Content-Type: application/sdp\n"
+ + "Content-Length: 142",
+ new byte[0]);
+ public static final String SIP_MESSAGE_1_TRANSACTION_ID = "z9hG4bK776asdhds";
+ public static final SipMessage SIP_MESSAGE_2 = new SipMessage(
+ "INVITE sip:bob@biloxi.com SIP/2.0",
+ // include leading whitespace.
+ " Max-Forwards: 70\n"
+ + "To: Bob <sip:bob@biloxi.com>\n"
+ + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+ + "Call-ID: a84b4c76e66710@pc33.atlanta.com\n"
+ + "CSeq: 314159 INVITE\n"
+ + "Contact: <sip:alice@pc33.atlanta.com>\n"
+ + "Content-Type: application/sdp\n"
+ + "Content-Length: 142\n"
+ // transaction ID should be returned for the first "Via"
+ + "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKabcdefghi\n"
+ + "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\n",
+
+ new byte[0]);
+ public static final String SIP_MESSAGE_2_TRANSACTION_ID = "z9hG4bKabcdefghi";
+ public static final SipMessage SIP_MESSAGE_3 = new SipMessage(
+ "INVITE sip:bob@biloxi.com SIP/2.0",
+ "Max-Forwards: 70\n"
+ + "To: Bob <sip:bob@biloxi.com>\n"
+ + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+ // Subject line is split into multiple lines via space and tab.
+ + "Subject: I know you're there,\n"
+ + " pick up the phone\n"
+ + "\tand talk to me!\n"
+ // transaction ID should be returned for the first "Via"
+ + "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKzyxwvutsr\n"
+ + "Call-ID: a84b4c76e66710@pc33.atlanta.com\n"
+ + "CSeq: 314159 INVITE\n"
+ + "Contact: <sip:alice@pc33.atlanta.com>\n"
+ + "Content-Type: application/sdp\n"
+ + "Content-Length: 142\n"
+ + "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKabcdefghi\n",
+ new byte[0]);
+ public static final String SIP_MESSAGE_3_TRANSACTION_ID = "z9hG4bKzyxwvutsr";
+ // compact form
+ public static final SipMessage SIP_MESSAGE_4 = new SipMessage(
+ "INVITE sip:bob@biloxi.com SIP/2.0",
+ "Max-Forwards: 70\n"
+ + "t: Bob <sip:bob@biloxi.com>\n"
+ + "f: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+ // compat form of via
+ + "v: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKAbCdEfGiJ\n"
+ + "v: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKabcdefghi\n"
+ + "i: a84b4c76e66710@pc33.atlanta.com\n"
+ + "CSeq: 314159 INVITE\n"
+ + "m: <sip:alice@pc33.atlanta.com>\n"
+ + "c: application/sdp\n"
+ + "l: 142\n",
+ new byte[0]);
+ public static final String SIP_MESSAGE_4_TRANSACTION_ID = "z9hG4bKAbCdEfGiJ";
+ public static final SipMessage SIP_MESSAGE_5 = new SipMessage(
+ "INVITE sip:bob@biloxi.com SIP/2.0",
+ "Max-Forwards: 70\n"
+ + "To: Bob <sip:bob@biloxi.com>\n"
+ + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+ // Malformed lines
+ + "Subject: I know you're there,\n"
+ + "pick up the phone\n"
+ + "and talk to me!\n"
+ // transaction ID should be returned for the first "Via"
+ + "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKzyxwvutsr\n"
+ + "Call-ID: a84b4c76e66710@pc33.atlanta.com\n"
+ + "CSeq: 314159 INVITE\n"
+ + "Contact: <sip:alice@pc33.atlanta.com>\n"
+ + "Content-Type: application/sdp\n"
+ + "Content-Length: 142\n"
+ + "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKabcdefghi\n",
+ new byte[0]);
+ public static final String SIP_MESSAGE_5_TRANSACTION_ID = "z9hG4bKzyxwvutsr";
+ // Not practical, but ensure that parsing works, even in special cases like one line.
+ public static final SipMessage SIP_MESSAGE_6 = new SipMessage(
+ "INVITE sip:bob@biloxi.com SIP/2.0",
+ "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKabcdefghi\n",
+ new byte[0]);
+ public static final String SIP_MESSAGE_6_TRANSACTION_ID = "z9hG4bKabcdefghi";
+ public static final SipMessage SIP_MESSAGE_7 = new SipMessage(
+ "INVITE sip:bob@biloxi.com SIP/2.0",
+ "Max-Forwards: 70\n"
+ + "To: Bob <sip:bob@biloxi.com>\n"
+ + "From: Alice <sip:alice@atlanta.com>;tag=1928301774\n"
+ + "Call-ID: a84b4c76e66710@pc33.atlanta.com\n"
+ + "CSeq: 314159 INVITE\n"
+ + "Contact: <sip:alice@pc33.atlanta.com>\n"
+ + "Content-Type: application/sdp\n"
+ + "Content-Length: 142\n"
+ // Typical Via, but on last line to test edge conditions
+ + "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds",
+ new byte[0]);
+ public static final String SIP_MESSAGE_7_TRANSACTION_ID = "z9hG4bK776asdhds";
+ // SIP Message from RFC 4475 "A Short Tortuous INVITE"
+ public static final SipMessage SIP_MESSAGE_8 = new SipMessage(
+ "INVITE sip:vivekg@chair-dnrc.example.com;unknownparam SIP/2.0",
+ "TO :\n"
+ + " sip:vivekg@chair-dnrc.example.com ; tag = 1918181833n\n"
+ + "from : \"J Rosenberg \\\\\\\"\" <sip:jdrosen@example.com>\n"
+ + " ;\n"
+ + " tag = 98asjd8\n"
+ + "MaX-fOrWaRdS: 0068\n"
+ + "Call-ID: wsinv.ndaksdj@192.0.2.1\n"
+ + "Content-Length : 150\n"
+ + "cseq: 0009\n"
+ + " INVITE\n"
+ + "s :\n"
+ + "NewFangledHeader: newfangled value\n"
+ + " continued newfangled value\n"
+ + "UnknownHeaderWithUnusualValue: ;;,,;;,;\n"
+ + "Content-Type: application/sdp\n"
+ + "Route:\n"
+ + " <sip:services.example.com;lr;unknownwith=value;unknown-no-value>\n"
+ // Note, this has multiple Via headers concatenated with one header key, we
+ // should return the first in the list.
+ + "v: SIP / 2.0 / TCP spindle.example.com ;\n"
+ + " branch = z9hG4bK9ikj8 ,\n"
+ + " SIP / 2.0 / UDP 192.168.255.111 ; branch=\n"
+ + " z9hG4bK30239\n"
+ + "Via : SIP / 2.0\n"
+ + " /UDP\n"
+ + " 192.0.2.2;branch=z9hG4bK390skdjuw\n"
+ + "m:\"Quoted string \\\"\\\"\" <sip:jdrosen@example.com> ; newparam =\n"
+ + " newvalue ;\n"
+ + " secondparam ; q = 0.33",
+ new byte[0]);
+ public static final String SIP_MESSAGE_8_TRANSACTION_ID = "z9hG4bK9ikj8";
+
+ @Test
+ @SmallTest
+ public void testGetTransactionId() {
+ assertEquals(SIP_MESSAGE_1_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
+ SIP_MESSAGE_1.getHeaderSection()));
+ assertEquals(SIP_MESSAGE_2_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
+ SIP_MESSAGE_2.getHeaderSection()));
+ assertEquals(SIP_MESSAGE_3_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
+ SIP_MESSAGE_3.getHeaderSection()));
+ assertEquals(SIP_MESSAGE_4_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
+ SIP_MESSAGE_4.getHeaderSection()));
+ assertEquals(SIP_MESSAGE_5_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
+ SIP_MESSAGE_5.getHeaderSection()));
+ assertEquals(SIP_MESSAGE_6_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
+ SIP_MESSAGE_6.getHeaderSection()));
+ assertEquals(SIP_MESSAGE_7_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
+ SIP_MESSAGE_7.getHeaderSection()));
+ assertEquals(SIP_MESSAGE_8_TRANSACTION_ID, SipMessageParsingUtils.getTransactionId(
+ SIP_MESSAGE_8.getHeaderSection()));
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
index 1097324..ee9e2b8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
@@ -167,7 +167,7 @@
restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);
// inject null sms pdu. This should cause intent to be received since pdu is null.
- mSmsDispatchersController.injectSmsPdu(null, SmsConstants.FORMAT_3GPP,
+ mSmsDispatchersController.injectSmsPdu(null, SmsConstants.FORMAT_3GPP, true,
(SmsDispatchersController.SmsInjectionCallback) result -> {
mInjectionCallbackTriggered = true;
assertEquals(Intents.RESULT_SMS_GENERIC_ERROR, result);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 0d00988..2aceb20 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -41,6 +41,8 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -59,6 +61,7 @@
import android.telephony.CarrierConfigManager;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyRegistryManager;
@@ -85,8 +88,10 @@
import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
import com.android.internal.telephony.imsphone.ImsPhone;
import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
+import com.android.internal.telephony.metrics.ImsStats;
import com.android.internal.telephony.metrics.MetricsCollector;
import com.android.internal.telephony.metrics.PersistAtomsStorage;
+import com.android.internal.telephony.metrics.SmsStats;
import com.android.internal.telephony.metrics.VoiceCallSessionStats;
import com.android.internal.telephony.test.SimulatedCommands;
import com.android.internal.telephony.test.SimulatedCommandsVerifier;
@@ -99,6 +104,7 @@
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
import com.android.internal.telephony.uicc.UiccProfile;
+import com.android.internal.telephony.uicc.UiccSlot;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.permission.PermissionManagerService;
@@ -296,7 +302,17 @@
@Mock
protected MetricsCollector mMetricsCollector;
@Mock
+ protected SmsStats mSmsStats;
+ @Mock
protected DataThrottler mDataThrottler;
+ @Mock
+ protected SignalStrength mSignalStrength;
+ @Mock
+ protected WifiManager mWifiManager;
+ @Mock
+ protected WifiInfo mWifiInfo;
+ @Mock
+ protected ImsStats mImsStats;
protected ActivityManager mActivityManager;
protected ImsCallProfile mImsCallProfile;
@@ -525,8 +541,11 @@
doReturn(mDataEnabledSettings).when(mPhone).getDataEnabledSettings();
doReturn(mDcTracker).when(mPhone).getDcTracker(anyInt());
doReturn(mCarrierPrivilegesTracker).when(mPhone).getCarrierPrivilegesTracker();
+ doReturn(mSignalStrength).when(mPhone).getSignalStrength();
doReturn(mVoiceCallSessionStats).when(mPhone).getVoiceCallSessionStats();
doReturn(mVoiceCallSessionStats).when(mImsPhone).getVoiceCallSessionStats();
+ doReturn(mSmsStats).when(mPhone).getSmsStats();
+ doReturn(mImsStats).when(mImsPhone).getImsStats();
mIccSmsInterfaceManager.mDispatchersController = mSmsDispatchersController;
//mUiccController
@@ -552,6 +571,7 @@
}
}
}).when(mUiccController).getIccRecords(anyInt(), anyInt());
+ doReturn(new UiccSlot[] {}).when(mUiccController).getUiccSlots();
//UiccCardApplication
doReturn(mSimRecords).when(mUiccCardApplication3gpp).getIccRecords();
@@ -615,6 +635,12 @@
doReturn(mNetworkRegistrationInfo).when(mServiceState).getNetworkRegistrationInfo(
anyInt(), anyInt());
doReturn(new HalVersion(1, 4)).when(mPhone).getHalVersion();
+ doReturn(2).when(mSignalStrength).getLevel();
+
+ // WiFi
+ doReturn(mWifiInfo).when(mWifiManager).getConnectionInfo();
+ doReturn(2).when(mWifiManager).calculateSignalLevel(anyInt());
+ doReturn(4).when(mWifiManager).getMaxSignalLevel();
//SIM
doReturn(1).when(mTelephonyManager).getSimCount();
@@ -644,6 +670,7 @@
// Metrics
doReturn(null).when(mContext).getFileStreamPath(anyString());
doReturn(mPersistAtomsStorage).when(mMetricsCollector).getAtomsStorage();
+ doReturn(mWifiManager).when(mContext).getSystemService(eq(Context.WIFI_SERVICE));
//Use reflection to mock singletons
replaceInstance(CallManager.class, "INSTANCE", null, mCallManager);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
index a6d30f9..68831f6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
@@ -130,18 +130,20 @@
"1234567890", /* displayAddress */
"This is the message body of a single-part message" /* messageBody */,
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
anyBoolean(), nullable(String.class), nullable(String.class),
- nullable(String.class), anyBoolean(), anyInt());
+ nullable(String.class), anyBoolean(), anyInt(), anyInt());
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(Cursor.class), anyBoolean());
@@ -234,12 +236,13 @@
blockedNumber, /* displayAddress */
"This is the message body of a single-part message" /* messageBody */,
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
anyBoolean(), nullable(String.class), nullable(String.class),
- nullable(String.class), anyBoolean(), anyInt());
+ nullable(String.class), anyBoolean(), anyInt(), anyInt());
mFakeBlockedNumberContentProvider.mBlockedNumbers.add(blockedNumber);
transitionFromStartupToIdle();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
index 40cb0be..2298f1b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -68,6 +68,7 @@
import com.android.internal.telephony.dataconnection.DataConnection.ConnectionParams;
import com.android.internal.telephony.dataconnection.DataConnection.DisconnectParams;
import com.android.internal.telephony.dataconnection.DataConnection.SetupResult;
+import com.android.internal.telephony.metrics.DataCallSessionStats;
import com.android.internal.util.IState;
import com.android.internal.util.StateMachine;
@@ -93,6 +94,8 @@
ApnContext mApnContext;
@Mock
DcFailBringUp mDcFailBringUp;
+ @Mock
+ DataCallSessionStats mDataCallSessionStats;
private DataConnection mDc;
private DataConnectionTestHandler mDataConnectionTestHandler;
@@ -311,6 +314,8 @@
mDataConnectionTestHandler.start();
waitForMs(200);
+ mDc.setDataCallSessionStats(mDataCallSessionStats);
+
logd("-Setup!");
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataThrottlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataThrottlerTest.java
index 44847c8..0af680d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataThrottlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataThrottlerTest.java
@@ -16,6 +16,9 @@
package com.android.internal.telephony.dataconnection;
+import static com.android.internal.telephony.dataconnection.DcTracker.REQUEST_TYPE_HANDOVER;
+import static com.android.internal.telephony.dataconnection.DcTracker.REQUEST_TYPE_NORMAL;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -23,7 +26,6 @@
import android.telephony.AccessNetworkConstants;
import android.telephony.data.ApnSetting;
import android.telephony.data.ApnThrottleStatus;
-import android.telephony.data.DataCallResponse;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -87,7 +89,7 @@
mDataThrottler.setRetryTime(ApnSetting.TYPE_DEFAULT, 1234567890L,
- DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN);
+ REQUEST_TYPE_NORMAL);
assertEquals(1234567890L, mDataThrottler.getRetryTime(ApnSetting.TYPE_DEFAULT));
assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY,
mDataThrottler.getRetryTime(ApnSetting.TYPE_MMS));
@@ -112,7 +114,7 @@
mDataThrottler.setRetryTime(ApnSetting.TYPE_DEFAULT | ApnSetting.TYPE_DUN, 13579L,
- DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER);
+ REQUEST_TYPE_HANDOVER);
assertEquals(13579L, mDataThrottler.getRetryTime(ApnSetting.TYPE_DEFAULT));
assertEquals(13579L, mDataThrottler.getRetryTime(ApnSetting.TYPE_DUN));
@@ -143,7 +145,7 @@
mDataThrottler.setRetryTime(ApnSetting.TYPE_MMS, -10,
- DataCallResponse.HANDOVER_FAILURE_MODE_UNKNOWN);
+ REQUEST_TYPE_NORMAL);
assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY,
mDataThrottler.getRetryTime(ApnSetting.TYPE_MMS));
processAllMessages();
@@ -158,8 +160,7 @@
));
mDataThrottler.setRetryTime(ApnSetting.TYPE_FOTA | ApnSetting.TYPE_EMERGENCY,
- RetryManager.NO_RETRY,
- DataCallResponse.HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER);
+ RetryManager.NO_RETRY, REQUEST_TYPE_HANDOVER);
processAllMessages();
expectedStatuses.add(List.of(
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
index 54d4c80..d201bcf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -231,12 +231,13 @@
"1234567890", /* displayAddress */
mMessageBody, /* messageBody */
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
anyBoolean(), nullable(String.class), nullable(String.class),
- nullable(String.class), anyBoolean(), anyInt());
+ nullable(String.class), anyBoolean(), anyInt(), anyInt());
createMockInboundSmsTracker();
@@ -469,12 +470,13 @@
"1234567890", /* displayAddress */
mMessageBody, /* messageBody */
true, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
anyBoolean(), nullable(String.class), nullable(String.class),
- nullable(String.class), anyBoolean(), anyInt());
+ nullable(String.class), anyBoolean(), anyInt(), anyInt());
mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS,
mInboundSmsTracker);
processAllMessages();
@@ -499,12 +501,13 @@
"1234567890", /* displayAddress */
mMessageBody, /* messageBody */
false, /* isClass0 */
- mSubId0));
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED));
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
anyBoolean(), nullable(String.class), nullable(String.class),
- nullable(String.class), anyBoolean(), anyInt());
+ nullable(String.class), anyBoolean(), anyInt(), anyInt());
doReturn(2131L).when(mInboundSmsTracker).getMessageId();
mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS,
mInboundSmsTracker);
@@ -561,7 +564,8 @@
is3gpp2WapPush, /* is3gpp2WapPdu */
mMessageBodyPart1, /* messageBody */
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
// Part 2
mInboundSmsTrackerPart2 = new InboundSmsTracker(
@@ -578,7 +582,8 @@
is3gpp2WapPush, /* is3gpp2WapPdu */
mMessageBodyPart2, /* messageBody */
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
}
@Test
@@ -602,7 +607,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// State machine should go back to idle and wait for second part
@@ -614,7 +620,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// State machine should go back to idle and wait for second part
@@ -630,7 +637,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// verify broadcast intents
@@ -659,7 +667,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// State machine should go back to idle and wait for second part
@@ -669,7 +678,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// verify broadcast intents
@@ -685,7 +695,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// verify no additional broadcasts sent
@@ -703,7 +714,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// verify no additional broadcasts sent
@@ -739,7 +751,8 @@
false, /* is3gpp2WapPdu */
mMessageBodyPart2, /* messageBody */
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
mSmsHeader.concatRef = new SmsHeader.ConcatRef();
doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader();
@@ -748,7 +761,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// State machine should go back to idle and wait for second part
@@ -758,7 +772,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// verify no broadcasts sent
@@ -789,7 +804,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// verify the message is stored in the raw table
@@ -814,13 +830,15 @@
false, /* is3gpp2WapPdu */
mMessageBodyPart2, /* messageBody */
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// verify no broadcasts sent
@@ -846,7 +864,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
@@ -857,7 +876,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
verify(mContext, never()).sendBroadcast(any(Intent.class));
@@ -890,7 +910,8 @@
false, /* is3gpp2WapPdu */
mMessageBodyPart1, /* messageBody */
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
mSmsHeader.concatRef = new SmsHeader.ConcatRef();
doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader();
@@ -898,7 +919,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
@@ -909,7 +931,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
verify(mContext, never()).sendBroadcast(any(Intent.class));
@@ -942,7 +965,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// State machine should go back to idle and wait for second part
@@ -952,7 +976,8 @@
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
nullable(String.class), nullable(String.class), anyInt(), anyInt(),
- anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt());
+ anyInt(), anyBoolean(), nullable(String.class), anyBoolean(), anyInt(),
+ anyInt());
sendNewSms();
// verify no broadcasts sent
@@ -1042,12 +1067,13 @@
"1234567890", /* displayAddress */
mMessageBody, /* messageBody */
false, /* isClass0 */
- mSubId0);
+ mSubId0,
+ InboundSmsHandler.SOURCE_NOT_INJECTED);
doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
.makeInboundSmsTracker(any(Context.class), nullable(byte[].class), anyLong(),
anyInt(), anyBoolean(),
anyBoolean(), nullable(String.class), nullable(String.class),
- nullable(String.class), anyBoolean(), anyInt());
+ nullable(String.class), anyBoolean(), anyInt(), anyInt());
//add a fake entry to db
ContentValues rawSms = new ContentValues();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
index 99c50d2..921a78b 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -157,6 +157,7 @@
mImsPhoneUT.registerForIncomingRing(mTestHandler,
EVENT_INCOMING_RING, null);
mImsPhoneUT.setVoiceCallSessionStats(mVoiceCallSessionStats);
+ mImsPhoneUT.setImsStats(mImsStats);
doReturn(mImsUtInterface).when(mImsCT).getUtInterface();
// When the mock GsmCdmaPhone gets setIsInEcbm called, ensure isInEcm matches.
doAnswer(invocation -> {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java
new file mode 100644
index 0000000..6223329
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/ImsStatsTest.java
@@ -0,0 +1,603 @@
+/*
+ * Copyright (C) 2020 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.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WLAN;
+import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
+import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO;
+import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
+import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.ProvisioningManager;
+import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
+import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
+import com.android.internal.telephony.uicc.IccCardStatus.CardState;
+import com.android.internal.telephony.uicc.UiccSlot;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+public class ImsStatsTest extends TelephonyTest {
+ private static final long START_TIME_MILLIS = 2000L;
+ private static final int CARRIER1_ID = 1;
+ private static final int CARRIER2_ID = 1187;
+
+ @MmTelCapability
+ private static final int CAPABILITY_TYPE_ALL =
+ MmTelCapabilities.CAPABILITY_TYPE_VOICE
+ | MmTelCapabilities.CAPABILITY_TYPE_VIDEO
+ | MmTelCapabilities.CAPABILITY_TYPE_SMS
+ | MmTelCapabilities.CAPABILITY_TYPE_UT;
+
+ @Mock private UiccSlot mPhysicalSlot0;
+ @Mock private UiccSlot mPhysicalSlot1;
+ @Mock private Phone mSecondPhone;
+ @Mock private ImsPhone mSecondImsPhone;
+
+ private TestableImsStats mImsStats;
+
+ private static class TestableImsStats extends ImsStats {
+ private long mTimeMillis = START_TIME_MILLIS;
+
+ TestableImsStats(ImsPhone imsPhone) {
+ super(imsPhone);
+ }
+
+ @Override
+ protected long getTimeMillis() {
+ // 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)
+ return mTimeMillis == 0L ? START_TIME_MILLIS : mTimeMillis;
+ }
+
+ private void setTimeMillis(long timeMillis) {
+ mTimeMillis = timeMillis;
+ }
+
+ private void incTimeMillis(long timeMillis) {
+ mTimeMillis += timeMillis;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+
+ doReturn(CARRIER1_ID).when(mPhone).getCarrierId();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
+ doReturn(mSST).when(mImsPhone).getServiceStateTracker();
+
+ // WWAN PS RAT is LTE
+ doReturn(
+ new NetworkRegistrationInfo.Builder()
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .build())
+ .when(mServiceState)
+ .getNetworkRegistrationInfo(DOMAIN_PS, TRANSPORT_TYPE_WWAN);
+
+ // Single physical SIM
+ doReturn(true).when(mPhysicalSlot0).isActive();
+ doReturn(CardState.CARDSTATE_PRESENT).when(mPhysicalSlot0).getCardState();
+ doReturn(false).when(mPhysicalSlot0).isEuicc();
+ doReturn(new UiccSlot[] {mPhysicalSlot0}).when(mUiccController).getUiccSlots();
+ doReturn(mPhysicalSlot0).when(mUiccController).getUiccSlot(0);
+ doReturn(mPhysicalSlot0).when(mUiccController).getUiccSlotForPhone(0);
+
+ mImsStats = new TestableImsStats(mImsPhone);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_registered() throws Exception {
+ // IMS over LTE
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VOICE,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VIDEO,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_UT,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_SMS,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+ mImsStats.onImsCapabilitiesChanged(
+ REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_ALL));
+
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.conclude();
+
+ // Duration should be counted
+ ArgumentCaptor<ImsRegistrationStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage).addImsRegistrationStats(captor.capture());
+ ImsRegistrationStats stats = captor.getValue();
+ assertEquals(CARRIER1_ID, stats.carrierId);
+ assertEquals(0, stats.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
+ assertEquals(2000L, stats.registeredMillis);
+ assertEquals(2000L, stats.voiceCapableMillis);
+ assertEquals(2000L, stats.voiceAvailableMillis);
+ assertEquals(2000L, stats.videoCapableMillis);
+ assertEquals(2000L, stats.videoAvailableMillis);
+ assertEquals(2000L, stats.utCapableMillis);
+ assertEquals(2000L, stats.utAvailableMillis);
+ assertEquals(2000L, stats.smsCapableMillis);
+ assertEquals(2000L, stats.smsAvailableMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_registeredPartialFeatures() throws Exception {
+ // IMS over LTE
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VOICE,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VIDEO,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_UT,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_SMS,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+ mImsStats.onImsCapabilitiesChanged(
+ REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
+
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.conclude();
+
+ // Duration should be counted
+ ArgumentCaptor<ImsRegistrationStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage).addImsRegistrationStats(captor.capture());
+ ImsRegistrationStats stats = captor.getValue();
+ assertEquals(CARRIER1_ID, stats.carrierId);
+ assertEquals(0, stats.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
+ assertEquals(2000L, stats.registeredMillis);
+ assertEquals(2000L, stats.voiceCapableMillis);
+ assertEquals(2000L, stats.voiceAvailableMillis);
+ assertEquals(2000L, stats.videoCapableMillis);
+ assertEquals(0L, stats.videoAvailableMillis);
+ assertEquals(2000L, stats.utCapableMillis);
+ assertEquals(0L, stats.utAvailableMillis);
+ assertEquals(2000L, stats.smsCapableMillis);
+ assertEquals(0L, stats.smsAvailableMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_registeredVoiceOnly() throws Exception {
+ // Wifi calling
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VOICE,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VOICE,
+ REGISTRATION_TECH_IWLAN,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VIDEO,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_UT,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WLAN);
+ mImsStats.onImsCapabilitiesChanged(
+ REGISTRATION_TECH_IWLAN, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
+
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.conclude();
+
+ // Duration should be counted
+ ArgumentCaptor<ImsRegistrationStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage).addImsRegistrationStats(captor.capture());
+ ImsRegistrationStats stats = captor.getValue();
+ assertEquals(CARRIER1_ID, stats.carrierId);
+ assertEquals(0, stats.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.rat);
+ assertEquals(2000L, stats.registeredMillis);
+ assertEquals(2000L, stats.voiceCapableMillis);
+ assertEquals(2000L, stats.voiceAvailableMillis);
+ assertEquals(0L, stats.videoCapableMillis);
+ assertEquals(0L, stats.videoAvailableMillis);
+ assertEquals(0L, stats.utCapableMillis);
+ assertEquals(0L, stats.utAvailableMillis);
+ assertEquals(0L, stats.smsCapableMillis);
+ assertEquals(0L, stats.smsAvailableMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_notRegistered() throws Exception {
+ // IMS over LTE
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VOICE,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VIDEO,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_UT,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_SMS,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onImsCapabilitiesChanged(
+ REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_ALL));
+
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.conclude();
+
+ // No atom should be generated
+ ArgumentCaptor<ImsRegistrationStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsCapabilitiesChanged_sameTech() throws Exception {
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VOICE,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.onImsCapabilitiesChanged(
+ REGISTRATION_TECH_LTE, new MmTelCapabilities(CAPABILITY_TYPE_VOICE));
+
+ // Atom with previous feature availability should be generated
+ ArgumentCaptor<ImsRegistrationStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage).addImsRegistrationStats(captor.capture());
+ ImsRegistrationStats stats = captor.getValue();
+ assertEquals(CARRIER1_ID, stats.carrierId);
+ assertEquals(0, stats.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
+ assertEquals(2000L, stats.registeredMillis);
+ assertEquals(2000L, stats.voiceCapableMillis);
+ assertEquals(0L, stats.voiceAvailableMillis);
+ assertEquals(0L, stats.videoCapableMillis);
+ assertEquals(0L, stats.videoAvailableMillis);
+ assertEquals(0L, stats.utCapableMillis);
+ assertEquals(0L, stats.utAvailableMillis);
+ assertEquals(0L, stats.smsCapableMillis);
+ assertEquals(0L, stats.smsAvailableMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onSetFeatureResponse_sameTech() throws Exception {
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VOICE,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.onSetFeatureResponse(
+ CAPABILITY_TYPE_VIDEO,
+ REGISTRATION_TECH_LTE,
+ ProvisioningManager.PROVISIONING_VALUE_ENABLED);
+
+ // Atom with previous capability should be generated
+ ArgumentCaptor<ImsRegistrationStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage).addImsRegistrationStats(captor.capture());
+ ImsRegistrationStats stats = captor.getValue();
+ assertEquals(CARRIER1_ID, stats.carrierId);
+ assertEquals(0, stats.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
+ assertEquals(2000L, stats.registeredMillis);
+ assertEquals(2000L, stats.voiceCapableMillis);
+ assertEquals(0L, stats.voiceAvailableMillis);
+ assertEquals(0L, stats.videoCapableMillis);
+ assertEquals(0L, stats.videoAvailableMillis);
+ assertEquals(0L, stats.utCapableMillis);
+ assertEquals(0L, stats.utAvailableMillis);
+ assertEquals(0L, stats.smsCapableMillis);
+ assertEquals(0L, stats.smsAvailableMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsRegistered_differentTech() throws Exception {
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WLAN);
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+
+ // At this point, the first 2 registrations should have their durations counted
+ ArgumentCaptor<ImsRegistrationStats> captor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage, times(2)).addImsRegistrationStats(captor.capture());
+ assertEquals(2, captor.getAllValues().size());
+ ImsRegistrationStats statsLte = captor.getAllValues().get(0);
+ assertEquals(CARRIER1_ID, statsLte.carrierId);
+ assertEquals(0, statsLte.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, statsLte.rat);
+ assertEquals(2000L, statsLte.registeredMillis);
+ assertEquals(0L, statsLte.voiceCapableMillis);
+ assertEquals(0L, statsLte.voiceAvailableMillis);
+ assertEquals(0L, statsLte.videoCapableMillis);
+ assertEquals(0L, statsLte.videoAvailableMillis);
+ assertEquals(0L, statsLte.utCapableMillis);
+ assertEquals(0L, statsLte.utAvailableMillis);
+ assertEquals(0L, statsLte.smsCapableMillis);
+ assertEquals(0L, statsLte.smsAvailableMillis);
+ ImsRegistrationStats statsWifi = captor.getAllValues().get(1);
+ assertEquals(CARRIER1_ID, statsWifi.carrierId);
+ assertEquals(0, statsWifi.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, statsWifi.rat);
+ assertEquals(2000L, statsWifi.registeredMillis);
+ assertEquals(0L, statsWifi.voiceCapableMillis);
+ assertEquals(0L, statsWifi.voiceAvailableMillis);
+ assertEquals(0L, statsWifi.videoCapableMillis);
+ assertEquals(0L, statsWifi.videoAvailableMillis);
+ assertEquals(0L, statsWifi.utCapableMillis);
+ assertEquals(0L, statsWifi.utAvailableMillis);
+ assertEquals(0L, statsWifi.smsCapableMillis);
+ assertEquals(0L, statsWifi.smsAvailableMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsUnregistered_setupFailure() throws Exception {
+ mImsStats.onImsUnregistered(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 999, "Timeout"));
+
+ // Atom with termination info should be generated
+ ArgumentCaptor<ImsRegistrationTermination> captor =
+ ArgumentCaptor.forClass(ImsRegistrationTermination.class);
+ verify(mPersistAtomsStorage).addImsRegistrationTermination(captor.capture());
+ ImsRegistrationTermination termination = captor.getValue();
+ assertEquals(CARRIER1_ID, termination.carrierId);
+ assertFalse(termination.isMultiSim);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, termination.ratAtEnd);
+ assertTrue(termination.setupFailed);
+ assertEquals(ImsReasonInfo.CODE_REGISTRATION_ERROR, termination.reasonCode);
+ assertEquals(999, termination.extraCode);
+ assertEquals("Timeout", termination.extraMessage);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsUnregistered_setupFailureWithProgress() throws Exception {
+ mImsStats.onImsRegistering(REGISTRATION_TECH_LTE);
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.onImsUnregistered(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 999, "Timeout"));
+
+ // Atom with termination info should be generated
+ ArgumentCaptor<ImsRegistrationTermination> captor =
+ ArgumentCaptor.forClass(ImsRegistrationTermination.class);
+ verify(mPersistAtomsStorage).addImsRegistrationTermination(captor.capture());
+ ImsRegistrationTermination termination = captor.getValue();
+ assertEquals(CARRIER1_ID, termination.carrierId);
+ assertFalse(termination.isMultiSim);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, termination.ratAtEnd);
+ assertTrue(termination.setupFailed);
+ assertEquals(ImsReasonInfo.CODE_REGISTRATION_ERROR, termination.reasonCode);
+ assertEquals(999, termination.extraCode);
+ assertEquals("Timeout", termination.extraMessage);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsUnregistered_afterRegistered() throws Exception {
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.onImsUnregistered(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 999, "Timeout"));
+
+ // Atom with termination info and durations should be generated
+ ArgumentCaptor<ImsRegistrationStats> statsCaptor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage).addImsRegistrationStats(statsCaptor.capture());
+ ImsRegistrationStats stats = statsCaptor.getValue();
+ assertEquals(CARRIER1_ID, stats.carrierId);
+ assertEquals(0, stats.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
+ assertEquals(2000L, stats.registeredMillis);
+ assertEquals(0L, stats.voiceCapableMillis);
+ assertEquals(0L, stats.voiceAvailableMillis);
+ assertEquals(0L, stats.videoCapableMillis);
+ assertEquals(0L, stats.videoAvailableMillis);
+ assertEquals(0L, stats.utCapableMillis);
+ assertEquals(0L, stats.utAvailableMillis);
+ assertEquals(0L, stats.smsCapableMillis);
+ assertEquals(0L, stats.smsAvailableMillis);
+ ArgumentCaptor<ImsRegistrationTermination> terminationCaptor =
+ ArgumentCaptor.forClass(ImsRegistrationTermination.class);
+ verify(mPersistAtomsStorage).addImsRegistrationTermination(terminationCaptor.capture());
+ ImsRegistrationTermination termination = terminationCaptor.getValue();
+ assertEquals(CARRIER1_ID, termination.carrierId);
+ assertFalse(termination.isMultiSim);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, termination.ratAtEnd);
+ assertFalse(termination.setupFailed);
+ assertEquals(ImsReasonInfo.CODE_REGISTRATION_ERROR, termination.reasonCode);
+ assertEquals(999, termination.extraCode);
+ assertEquals("Timeout", termination.extraMessage);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsUnregistered_nullMessage() throws Exception {
+ mImsStats.onImsUnregistered(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 0, null));
+
+ // Atom with termination info should be generated, null string should be sanitized
+ ArgumentCaptor<ImsRegistrationTermination> captor =
+ ArgumentCaptor.forClass(ImsRegistrationTermination.class);
+ verify(mPersistAtomsStorage).addImsRegistrationTermination(captor.capture());
+ ImsRegistrationTermination termination = captor.getValue();
+ assertEquals(CARRIER1_ID, termination.carrierId);
+ assertFalse(termination.isMultiSim);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, termination.ratAtEnd);
+ assertTrue(termination.setupFailed);
+ assertEquals(ImsReasonInfo.CODE_REGISTRATION_ERROR, termination.reasonCode);
+ assertEquals(0, termination.extraCode);
+ assertEquals("", termination.extraMessage);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsUnregistered_longMessage() throws Exception {
+ String longExtraMessage =
+ "This message is too long -- it has more than 128 characters: "
+ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ + " This is the end of the message.";
+ mImsStats.onImsUnregistered(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 0, longExtraMessage));
+
+ // Atom with termination info should be generated, null string should be sanitized
+ ArgumentCaptor<ImsRegistrationTermination> captor =
+ ArgumentCaptor.forClass(ImsRegistrationTermination.class);
+ verify(mPersistAtomsStorage).addImsRegistrationTermination(captor.capture());
+ ImsRegistrationTermination termination = captor.getValue();
+ assertEquals(CARRIER1_ID, termination.carrierId);
+ assertFalse(termination.isMultiSim);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, termination.ratAtEnd);
+ assertTrue(termination.setupFailed);
+ assertEquals(ImsReasonInfo.CODE_REGISTRATION_ERROR, termination.reasonCode);
+ assertEquals(0, termination.extraCode);
+ assertEquals(128, termination.extraMessage.length());
+ assertTrue(longExtraMessage.startsWith(termination.extraMessage));
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onImsUnregistered_multiSim() throws Exception {
+ doReturn(mSecondImsPhone).when(mSecondPhone).getImsPhone();
+ doReturn(mSecondPhone).when(mSecondImsPhone).getDefaultPhone();
+ doReturn(1).when(mSecondPhone).getPhoneId();
+ doReturn(1).when(mSecondImsPhone).getPhoneId();
+ doReturn(CARRIER2_ID).when(mSecondPhone).getCarrierId();
+ doReturn(true).when(mPhysicalSlot1).isActive();
+ doReturn(CardState.CARDSTATE_PRESENT).when(mPhysicalSlot1).getCardState();
+ doReturn(false).when(mPhysicalSlot1).isEuicc();
+ doReturn(new UiccSlot[] {mPhysicalSlot0, mPhysicalSlot1})
+ .when(mUiccController)
+ .getUiccSlots();
+ doReturn(mPhysicalSlot1).when(mUiccController).getUiccSlot(1);
+ doReturn(mPhysicalSlot1).when(mUiccController).getUiccSlotForPhone(1);
+ // Reusing service state tracker from phone 0 for simplicity
+ doReturn(mSST).when(mSecondPhone).getServiceStateTracker();
+ doReturn(mSST).when(mSecondImsPhone).getServiceStateTracker();
+ mImsStats = new TestableImsStats(mSecondImsPhone);
+ mImsStats.onImsRegistered(TRANSPORT_TYPE_WWAN);
+ mImsStats.incTimeMillis(2000L);
+ mImsStats.onImsUnregistered(
+ new ImsReasonInfo(ImsReasonInfo.CODE_REGISTRATION_ERROR, 999, "Timeout"));
+
+ // Atom with termination info and durations should be generated
+ ArgumentCaptor<ImsRegistrationStats> statsCaptor =
+ ArgumentCaptor.forClass(ImsRegistrationStats.class);
+ verify(mPersistAtomsStorage).addImsRegistrationStats(statsCaptor.capture());
+ ImsRegistrationStats stats = statsCaptor.getValue();
+ assertEquals(CARRIER2_ID, stats.carrierId);
+ assertEquals(1, stats.simSlotIndex);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.rat);
+ assertEquals(2000L, stats.registeredMillis);
+ assertEquals(0L, stats.voiceCapableMillis);
+ assertEquals(0L, stats.voiceAvailableMillis);
+ assertEquals(0L, stats.videoCapableMillis);
+ assertEquals(0L, stats.videoAvailableMillis);
+ assertEquals(0L, stats.utCapableMillis);
+ assertEquals(0L, stats.utAvailableMillis);
+ assertEquals(0L, stats.smsCapableMillis);
+ assertEquals(0L, stats.smsAvailableMillis);
+ ArgumentCaptor<ImsRegistrationTermination> terminationCaptor =
+ ArgumentCaptor.forClass(ImsRegistrationTermination.class);
+ verify(mPersistAtomsStorage).addImsRegistrationTermination(terminationCaptor.capture());
+ ImsRegistrationTermination termination = terminationCaptor.getValue();
+ assertEquals(CARRIER2_ID, termination.carrierId);
+ assertTrue(termination.isMultiSim);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, termination.ratAtEnd);
+ assertFalse(termination.setupFailed);
+ assertEquals(ImsReasonInfo.CODE_REGISTRATION_ERROR, termination.reasonCode);
+ assertEquals(999, termination.extraCode);
+ assertEquals("Timeout", termination.extraMessage);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
index 103cae1..cd15686 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/MetricsCollectorTest.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony.metrics;
+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.SIM_SLOT_STATE;
import static com.android.internal.telephony.TelephonyStatsLog.SUPPORTED_RADIO_ACCESS_FAMILY;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_RAT_USAGE;
@@ -38,7 +40,9 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage;
+import com.android.internal.telephony.nano.PersistAtomsProto.CellularDataServiceSwitch;
+import com.android.internal.telephony.nano.PersistAtomsProto.CellularServiceState;
+import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallRatUsage;
import com.android.internal.telephony.nano.PersistAtomsProto.VoiceCallSession;
import com.android.internal.telephony.uicc.IccCardStatus.CardState;
import com.android.internal.telephony.uicc.UiccCard;
@@ -82,6 +86,8 @@
@Mock private UiccSlot mEsimSlot;
@Mock private UiccCard mActiveCard;
+ @Mock private ServiceStateStats mServiceStateStats;
+
private MetricsCollector mMetricsCollector;
@Before
@@ -89,6 +95,8 @@
super.setUp(getClass().getSimpleName());
mMetricsCollector = new MetricsCollector(mContext);
mMetricsCollector.setPersistAtomsStorage(mPersistAtomsStorage);
+ doReturn(mSST).when(mSecondPhone).getServiceStateTracker();
+ doReturn(mServiceStateStats).when(mSST).getServiceStateStats();
}
@After
@@ -216,7 +224,7 @@
@Test
@SmallTest
public void onPullAtom_voiceCallRatUsage_empty() throws Exception {
- doReturn(new RawVoiceCallRatUsage[0])
+ doReturn(new VoiceCallRatUsage[0])
.when(mPersistAtomsStorage)
.getVoiceCallRatUsages(anyLong());
List<StatsEvent> actualAtoms = new ArrayList<>();
@@ -244,11 +252,11 @@
@Test
@SmallTest
public void onPullAtom_voiceCallRatUsage_bucketWithTooFewCalls() throws Exception {
- RawVoiceCallRatUsage usage1 = new RawVoiceCallRatUsage();
+ VoiceCallRatUsage usage1 = new VoiceCallRatUsage();
usage1.callCount = MIN_CALLS_PER_BUCKET;
- RawVoiceCallRatUsage usage2 = new RawVoiceCallRatUsage();
+ VoiceCallRatUsage usage2 = new VoiceCallRatUsage();
usage2.callCount = MIN_CALLS_PER_BUCKET - 1L;
- doReturn(new RawVoiceCallRatUsage[] {usage1, usage1, usage1, usage2})
+ doReturn(new VoiceCallRatUsage[] {usage1, usage1, usage1, usage2})
.when(mPersistAtomsStorage)
.getVoiceCallRatUsages(anyLong());
List<StatsEvent> actualAtoms = new ArrayList<>();
@@ -303,4 +311,95 @@
assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
// TODO(b/153196254): verify atom contents
}
+
+ @Test
+ @SmallTest
+ public void onPullAtom_cellularDataServiceSwitch_empty() throws Exception {
+ doReturn(new CellularDataServiceSwitch[0])
+ .when(mPersistAtomsStorage)
+ .getCellularDataServiceSwitches(anyLong());
+ List<StatsEvent> actualAtoms = new ArrayList<>();
+
+ int result = mMetricsCollector.onPullAtom(CELLULAR_DATA_SERVICE_SWITCH, actualAtoms);
+
+ assertThat(actualAtoms).hasSize(0);
+ assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+ // TODO(b/153196254): verify atom contents
+ }
+
+ @Test
+ @SmallTest
+ public void onPullAtom_cellularDataServiceSwitch_tooFrequent() throws Exception {
+ doReturn(null).when(mPersistAtomsStorage).getCellularDataServiceSwitches(anyLong());
+ List<StatsEvent> actualAtoms = new ArrayList<>();
+
+ int result = mMetricsCollector.onPullAtom(CELLULAR_DATA_SERVICE_SWITCH, actualAtoms);
+
+ assertThat(actualAtoms).hasSize(0);
+ assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
+ verify(mPersistAtomsStorage, times(1))
+ .getCellularDataServiceSwitches(eq(MIN_COOLDOWN_MILLIS));
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onPullAtom_cellularDataServiceSwitch_multipleSwitches() throws Exception {
+ CellularDataServiceSwitch serviceSwitch = new CellularDataServiceSwitch();
+ doReturn(new CellularDataServiceSwitch[] {serviceSwitch, serviceSwitch, serviceSwitch})
+ .when(mPersistAtomsStorage)
+ .getCellularDataServiceSwitches(anyLong());
+ List<StatsEvent> actualAtoms = new ArrayList<>();
+
+ int result = mMetricsCollector.onPullAtom(CELLULAR_DATA_SERVICE_SWITCH, actualAtoms);
+
+ assertThat(actualAtoms).hasSize(3);
+ assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+ // TODO(b/153196254): verify atom contents
+ }
+
+ @Test
+ @SmallTest
+ public void onPullAtom_cellularServiceState_empty() throws Exception {
+ doReturn(new CellularServiceState[0])
+ .when(mPersistAtomsStorage)
+ .getCellularServiceStates(anyLong());
+ List<StatsEvent> actualAtoms = new ArrayList<>();
+
+ int result = mMetricsCollector.onPullAtom(CELLULAR_SERVICE_STATE, actualAtoms);
+
+ assertThat(actualAtoms).hasSize(0);
+ assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+ // TODO(b/153196254): verify atom contents
+ }
+
+ @Test
+ @SmallTest
+ public void onPullAtom_cellularServiceState_tooFrequent() throws Exception {
+ doReturn(null).when(mPersistAtomsStorage).getCellularServiceStates(anyLong());
+ List<StatsEvent> actualAtoms = new ArrayList<>();
+
+ int result = mMetricsCollector.onPullAtom(CELLULAR_SERVICE_STATE, actualAtoms);
+
+ assertThat(actualAtoms).hasSize(0);
+ assertThat(result).isEqualTo(StatsManager.PULL_SKIP);
+ verify(mPersistAtomsStorage, times(1)).getCellularServiceStates(eq(MIN_COOLDOWN_MILLIS));
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void onPullAtom_cellularServiceState_multipleStates() throws Exception {
+ CellularServiceState state = new CellularServiceState();
+ doReturn(new CellularServiceState[] {state, state, state})
+ .when(mPersistAtomsStorage)
+ .getCellularServiceStates(anyLong());
+ List<StatsEvent> actualAtoms = new ArrayList<>();
+
+ int result = mMetricsCollector.onPullAtom(CELLULAR_SERVICE_STATE, actualAtoms);
+
+ assertThat(actualAtoms).hasSize(3);
+ assertThat(result).isEqualTo(StatsManager.PULL_SUCCESS);
+ // TODO(b/153196254): verify atom contents
+ }
}
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 ac1d73f..54acb92 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java
@@ -38,13 +38,18 @@
import android.annotation.Nullable;
import android.content.Context;
import android.telephony.DisconnectCause;
+import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsReasonInfo;
import android.test.suitebuilder.annotation.SmallTest;
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.ImsRegistrationStats;
+import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination;
import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms;
-import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage;
+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;
import com.android.internal.telephony.protobuf.nano.MessageNano;
@@ -63,6 +68,8 @@
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.Queue;
public class PersistAtomsStorageTest extends TelephonyTest {
private static final String TEST_FILE = "PersistAtomsStorageTest.pb";
@@ -88,14 +95,39 @@
// failed call
private VoiceCallSession mCall4Proto;
- private RawVoiceCallRatUsage mCarrier1LteUsageProto;
- private RawVoiceCallRatUsage mCarrier1UmtsUsageProto;
- private RawVoiceCallRatUsage mCarrier2LteUsageProto;
- private RawVoiceCallRatUsage mCarrier3LteUsageProto;
- private RawVoiceCallRatUsage mCarrier3GsmUsageProto;
+ private VoiceCallRatUsage mCarrier1LteUsageProto;
+ private VoiceCallRatUsage mCarrier1UmtsUsageProto;
+ private VoiceCallRatUsage mCarrier2LteUsageProto;
+ private VoiceCallRatUsage mCarrier3LteUsageProto;
+ private VoiceCallRatUsage mCarrier3GsmUsageProto;
private VoiceCallSession[] mVoiceCallSessions;
- private RawVoiceCallRatUsage[] mVoiceCallRatUsages;
+ private VoiceCallRatUsage[] mVoiceCallRatUsages;
+
+ // data service state switch for slot 0 and 1
+ private CellularDataServiceSwitch mServiceSwitch1Proto;
+ private CellularDataServiceSwitch mServiceSwitch2Proto;
+
+ // service states for slot 0 and 1
+ private CellularServiceState mServiceState1Proto;
+ private CellularServiceState mServiceState2Proto;
+ private CellularServiceState mServiceState3Proto;
+ private CellularServiceState mServiceState4Proto;
+
+ private CellularDataServiceSwitch[] mServiceSwitches;
+ private CellularServiceState[] mServiceStates;
+
+ // IMS registrations for slot 0 and 1
+ private ImsRegistrationStats mImsRegistrationStatsLte0;
+ private ImsRegistrationStats mImsRegistrationStatsWifi0;
+ private ImsRegistrationStats mImsRegistrationStatsLte1;
+
+ // IMS registration terminations for slot 0 and 1
+ private ImsRegistrationTermination mImsRegistrationTerminationLte;
+ private ImsRegistrationTermination mImsRegistrationTerminationWifi;
+
+ private ImsRegistrationStats[] mImsRegistrationStats;
+ private ImsRegistrationTermination[] mImsRegistrationTerminations;
private void makeTestData() {
// MO call with SRVCC (LTE to UMTS)
@@ -213,38 +245,38 @@
mCall4Proto.isEmergency = false;
mCall4Proto.isRoaming = true;
- mCarrier1LteUsageProto = new RawVoiceCallRatUsage();
+ mCarrier1LteUsageProto = new VoiceCallRatUsage();
mCarrier1LteUsageProto.carrierId = CARRIER1_ID;
mCarrier1LteUsageProto.rat = TelephonyManager.NETWORK_TYPE_LTE;
mCarrier1LteUsageProto.callCount = 1L;
mCarrier1LteUsageProto.totalDurationMillis = 8000L;
- mCarrier1UmtsUsageProto = new RawVoiceCallRatUsage();
+ mCarrier1UmtsUsageProto = new VoiceCallRatUsage();
mCarrier1UmtsUsageProto.carrierId = CARRIER1_ID;
mCarrier1UmtsUsageProto.rat = TelephonyManager.NETWORK_TYPE_UMTS;
mCarrier1UmtsUsageProto.callCount = 1L;
mCarrier1UmtsUsageProto.totalDurationMillis = 6000L;
- mCarrier2LteUsageProto = new RawVoiceCallRatUsage();
+ mCarrier2LteUsageProto = new VoiceCallRatUsage();
mCarrier2LteUsageProto.carrierId = CARRIER2_ID;
mCarrier2LteUsageProto.rat = TelephonyManager.NETWORK_TYPE_LTE;
mCarrier2LteUsageProto.callCount = 2L;
mCarrier2LteUsageProto.totalDurationMillis = 20000L;
- mCarrier3LteUsageProto = new RawVoiceCallRatUsage();
+ mCarrier3LteUsageProto = new VoiceCallRatUsage();
mCarrier3LteUsageProto.carrierId = CARRIER3_ID;
mCarrier3LteUsageProto.rat = TelephonyManager.NETWORK_TYPE_LTE;
mCarrier3LteUsageProto.callCount = 1L;
mCarrier3LteUsageProto.totalDurationMillis = 1000L;
- mCarrier3GsmUsageProto = new RawVoiceCallRatUsage();
+ mCarrier3GsmUsageProto = new VoiceCallRatUsage();
mCarrier3GsmUsageProto.carrierId = CARRIER3_ID;
mCarrier3GsmUsageProto.rat = TelephonyManager.NETWORK_TYPE_GSM;
mCarrier3GsmUsageProto.callCount = 1L;
mCarrier3GsmUsageProto.totalDurationMillis = 100000L;
mVoiceCallRatUsages =
- new RawVoiceCallRatUsage[] {
+ new VoiceCallRatUsage[] {
mCarrier1UmtsUsageProto,
mCarrier1LteUsageProto,
mCarrier2LteUsageProto,
@@ -253,6 +285,152 @@
};
mVoiceCallSessions =
new VoiceCallSession[] {mCall1Proto, mCall2Proto, mCall3Proto, mCall4Proto};
+
+ // OOS to LTE on slot 0
+ mServiceSwitch1Proto = new CellularDataServiceSwitch();
+ mServiceSwitch1Proto.ratFrom = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ mServiceSwitch1Proto.ratTo = TelephonyManager.NETWORK_TYPE_LTE;
+ mServiceSwitch1Proto.simSlotIndex = 0;
+ mServiceSwitch1Proto.isMultiSim = true;
+ mServiceSwitch1Proto.carrierId = CARRIER1_ID;
+ mServiceSwitch1Proto.switchCount = 1;
+
+ // LTE to UMTS on slot 1
+ mServiceSwitch2Proto = new CellularDataServiceSwitch();
+ mServiceSwitch2Proto.ratFrom = TelephonyManager.NETWORK_TYPE_LTE;
+ mServiceSwitch2Proto.ratTo = TelephonyManager.NETWORK_TYPE_UMTS;
+ mServiceSwitch2Proto.simSlotIndex = 0;
+ mServiceSwitch2Proto.isMultiSim = true;
+ mServiceSwitch2Proto.carrierId = CARRIER2_ID;
+ mServiceSwitch2Proto.switchCount = 2;
+
+ // OOS on slot 0
+ mServiceState1Proto = new CellularServiceState();
+ mServiceState1Proto.voiceRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ mServiceState1Proto.dataRat = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ mServiceState1Proto.voiceRoamingType = ServiceState.ROAMING_TYPE_NOT_ROAMING;
+ mServiceState1Proto.dataRoamingType = ServiceState.ROAMING_TYPE_NOT_ROAMING;
+ mServiceState1Proto.isEndc = false;
+ mServiceState1Proto.simSlotIndex = 0;
+ mServiceState1Proto.isMultiSim = true;
+ mServiceState1Proto.carrierId = CARRIER1_ID;
+ mServiceState1Proto.totalTimeMillis = 5000L;
+
+ // LTE with ENDC on slot 0
+ mServiceState2Proto = new CellularServiceState();
+ mServiceState2Proto.voiceRat = TelephonyManager.NETWORK_TYPE_LTE;
+ mServiceState2Proto.dataRat = TelephonyManager.NETWORK_TYPE_LTE;
+ mServiceState2Proto.voiceRoamingType = ServiceState.ROAMING_TYPE_NOT_ROAMING;
+ mServiceState2Proto.dataRoamingType = ServiceState.ROAMING_TYPE_NOT_ROAMING;
+ mServiceState2Proto.isEndc = true;
+ mServiceState2Proto.simSlotIndex = 0;
+ mServiceState2Proto.isMultiSim = true;
+ mServiceState2Proto.carrierId = CARRIER1_ID;
+ mServiceState2Proto.totalTimeMillis = 15000L;
+
+ // LTE with WFC and roaming on slot 1
+ mServiceState3Proto = new CellularServiceState();
+ mServiceState3Proto.voiceRat = TelephonyManager.NETWORK_TYPE_IWLAN;
+ mServiceState3Proto.dataRat = TelephonyManager.NETWORK_TYPE_LTE;
+ mServiceState3Proto.voiceRoamingType = ServiceState.ROAMING_TYPE_INTERNATIONAL;
+ mServiceState3Proto.dataRoamingType = ServiceState.ROAMING_TYPE_INTERNATIONAL;
+ mServiceState3Proto.isEndc = false;
+ mServiceState3Proto.simSlotIndex = 1;
+ mServiceState3Proto.isMultiSim = true;
+ mServiceState3Proto.carrierId = CARRIER2_ID;
+ mServiceState3Proto.totalTimeMillis = 10000L;
+
+ // UMTS with roaming on slot 1
+ mServiceState4Proto = new CellularServiceState();
+ mServiceState4Proto.voiceRat = TelephonyManager.NETWORK_TYPE_UMTS;
+ mServiceState4Proto.dataRat = TelephonyManager.NETWORK_TYPE_UMTS;
+ mServiceState4Proto.voiceRoamingType = ServiceState.ROAMING_TYPE_INTERNATIONAL;
+ mServiceState4Proto.dataRoamingType = ServiceState.ROAMING_TYPE_INTERNATIONAL;
+ mServiceState4Proto.isEndc = false;
+ mServiceState4Proto.simSlotIndex = 1;
+ mServiceState4Proto.isMultiSim = true;
+ mServiceState4Proto.carrierId = CARRIER2_ID;
+ mServiceState4Proto.totalTimeMillis = 10000L;
+
+ mServiceSwitches =
+ new CellularDataServiceSwitch[] {mServiceSwitch1Proto, mServiceSwitch2Proto};
+ mServiceStates =
+ new CellularServiceState[] {
+ mServiceState1Proto,
+ mServiceState2Proto,
+ mServiceState3Proto,
+ mServiceState4Proto
+ };
+
+ // IMS over LTE on slot 0, registered for 5 seconds
+ mImsRegistrationStatsLte0 = new ImsRegistrationStats();
+ mImsRegistrationStatsLte0.carrierId = CARRIER1_ID;
+ mImsRegistrationStatsLte0.simSlotIndex = 0;
+ mImsRegistrationStatsLte0.rat = TelephonyManager.NETWORK_TYPE_LTE;
+ mImsRegistrationStatsLte0.registeredMillis = 5000L;
+ mImsRegistrationStatsLte0.voiceCapableMillis = 5000L;
+ mImsRegistrationStatsLte0.voiceAvailableMillis = 5000L;
+ mImsRegistrationStatsLte0.smsCapableMillis = 5000L;
+ mImsRegistrationStatsLte0.smsAvailableMillis = 5000L;
+ mImsRegistrationStatsLte0.videoCapableMillis = 5000L;
+ mImsRegistrationStatsLte0.videoAvailableMillis = 5000L;
+ mImsRegistrationStatsLte0.utCapableMillis = 5000L;
+ mImsRegistrationStatsLte0.utAvailableMillis = 5000L;
+
+ // IMS over WiFi on slot 0, registered for 10 seconds (voice only)
+ mImsRegistrationStatsWifi0 = new ImsRegistrationStats();
+ mImsRegistrationStatsWifi0.carrierId = CARRIER2_ID;
+ mImsRegistrationStatsWifi0.simSlotIndex = 0;
+ mImsRegistrationStatsWifi0.rat = TelephonyManager.NETWORK_TYPE_IWLAN;
+ mImsRegistrationStatsWifi0.registeredMillis = 10000L;
+ mImsRegistrationStatsWifi0.voiceCapableMillis = 10000L;
+ mImsRegistrationStatsWifi0.voiceAvailableMillis = 10000L;
+
+ // IMS over LTE on slot 1, registered for 20 seconds
+ mImsRegistrationStatsLte1 = new ImsRegistrationStats();
+ mImsRegistrationStatsLte1.carrierId = CARRIER1_ID;
+ mImsRegistrationStatsLte1.simSlotIndex = 0;
+ mImsRegistrationStatsLte1.rat = TelephonyManager.NETWORK_TYPE_LTE;
+ mImsRegistrationStatsLte1.registeredMillis = 20000L;
+ mImsRegistrationStatsLte1.voiceCapableMillis = 20000L;
+ mImsRegistrationStatsLte1.voiceAvailableMillis = 20000L;
+ mImsRegistrationStatsLte1.smsCapableMillis = 20000L;
+ mImsRegistrationStatsLte1.smsAvailableMillis = 20000L;
+ mImsRegistrationStatsLte1.videoCapableMillis = 20000L;
+ mImsRegistrationStatsLte1.videoAvailableMillis = 20000L;
+ mImsRegistrationStatsLte1.utCapableMillis = 20000L;
+ mImsRegistrationStatsLte1.utAvailableMillis = 20000L;
+
+ // IMS terminations on LTE
+ mImsRegistrationTerminationLte = new ImsRegistrationTermination();
+ mImsRegistrationTerminationLte.carrierId = CARRIER1_ID;
+ mImsRegistrationTerminationLte.isMultiSim = true;
+ mImsRegistrationTerminationLte.ratAtEnd = TelephonyManager.NETWORK_TYPE_LTE;
+ mImsRegistrationTerminationLte.setupFailed = false;
+ mImsRegistrationTerminationLte.reasonCode = ImsReasonInfo.CODE_REGISTRATION_ERROR;
+ mImsRegistrationTerminationLte.extraCode = 999;
+ mImsRegistrationTerminationLte.extraMessage = "Request Timeout";
+ mImsRegistrationTerminationLte.count = 2;
+
+ // IMS terminations on WiFi
+ mImsRegistrationTerminationWifi = new ImsRegistrationTermination();
+ mImsRegistrationTerminationWifi.carrierId = CARRIER2_ID;
+ mImsRegistrationTerminationWifi.isMultiSim = true;
+ mImsRegistrationTerminationWifi.ratAtEnd = TelephonyManager.NETWORK_TYPE_IWLAN;
+ mImsRegistrationTerminationWifi.setupFailed = false;
+ mImsRegistrationTerminationWifi.reasonCode = ImsReasonInfo.CODE_REGISTRATION_ERROR;
+ mImsRegistrationTerminationWifi.extraCode = 0;
+ mImsRegistrationTerminationWifi.extraMessage = "";
+ mImsRegistrationTerminationWifi.count = 1;
+
+ mImsRegistrationStats =
+ new ImsRegistrationStats[] {
+ mImsRegistrationStatsLte0, mImsRegistrationStatsWifi0, mImsRegistrationStatsLte1
+ };
+ mImsRegistrationTerminations =
+ new ImsRegistrationTermination[] {
+ mImsRegistrationTerminationLte, mImsRegistrationTerminationWifi
+ };
}
private static class TestablePersistAtomsStorage extends PersistAtomsStorage {
@@ -260,6 +438,8 @@
TestablePersistAtomsStorage(Context context) {
super(context);
+ // Remove delay for saving to persistent storage during tests.
+ mSaveDelay = 0;
}
@Override
@@ -278,7 +458,8 @@
}
private PersistAtoms getAtomsProto() {
- // NOTE: not guarded by mLock as usual, should be fine since the test is single-threaded
+ // NOTE: unlike other methods in PersistAtomsStorage, this is not synchronized, but
+ // should be fine since the test is single-threaded
return mAtoms;
}
}
@@ -329,18 +510,8 @@
mPersistAtomsStorage.incTimeMillis(100L);
// no exception should be thrown, storage should be empty, pull time should be start time
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis);
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertNotNull(voiceCallRatUsage);
- assertEquals(0, voiceCallRatUsage.length);
- assertNotNull(voiceCallSession);
- assertEquals(0, voiceCallSession.length);
+ assertAllPullTimestampEquals(START_TIME_MILLIS);
+ assertStorageIsEmptyForAllAtoms();
}
@Test
@@ -353,18 +524,8 @@
mPersistAtomsStorage.incTimeMillis(100L);
// no exception should be thrown, storage should be empty, pull time should be start time
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis);
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertNotNull(voiceCallRatUsage);
- assertEquals(0, voiceCallRatUsage.length);
- assertNotNull(voiceCallSession);
- assertEquals(0, voiceCallSession.length);
+ assertAllPullTimestampEquals(START_TIME_MILLIS);
+ assertStorageIsEmptyForAllAtoms();
}
@Test
@@ -376,18 +537,8 @@
mPersistAtomsStorage.incTimeMillis(100L);
// no exception should be thrown, storage should be empty, pull time should be start time
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis);
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertNotNull(voiceCallRatUsage);
- assertEquals(0, voiceCallRatUsage.length);
- assertNotNull(voiceCallSession);
- assertEquals(0, voiceCallSession.length);
+ assertAllPullTimestampEquals(START_TIME_MILLIS);
+ assertStorageIsEmptyForAllAtoms();
}
@Test
@@ -401,57 +552,44 @@
mPersistAtomsStorage.incTimeMillis(100L);
// no exception should be thrown, storage should be empty, pull time should be start time
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis);
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertNotNull(voiceCallRatUsage);
- assertEquals(0, voiceCallRatUsage.length);
- assertNotNull(voiceCallSession);
- assertEquals(0, voiceCallSession.length);
+ assertAllPullTimestampEquals(START_TIME_MILLIS);
+ assertStorageIsEmptyForAllAtoms();
}
@Test
@SmallTest
public void loadAtoms_pullTimeMissing() throws Exception {
+ // create test file with lastPullTimeMillis = 0L, i.e. default/unknown
createTestFile(0L);
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
mPersistAtomsStorage.incTimeMillis(100L);
// no exception should be thrown, storage should be match, pull time should be start time
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis);
- assertEquals(
- START_TIME_MILLIS,
- mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertProtoArrayEquals(mVoiceCallRatUsages, voiceCallRatUsage);
- assertProtoArrayEquals(mVoiceCallSessions, voiceCallSession);
+ assertAllPullTimestampEquals(START_TIME_MILLIS);
+ assertProtoArrayEquals(mVoiceCallRatUsages, mPersistAtomsStorage.getVoiceCallRatUsages(0L));
+ assertProtoArrayEquals(mVoiceCallSessions, mPersistAtomsStorage.getVoiceCallSessions(0L));
+ assertProtoArrayEqualsIgnoringOrder(
+ mServiceStates, mPersistAtomsStorage.getCellularServiceStates(0L));
+ assertProtoArrayEqualsIgnoringOrder(
+ mServiceSwitches, mPersistAtomsStorage.getCellularDataServiceSwitches(0L));
}
@Test
@SmallTest
public void loadAtoms_validContents() throws Exception {
- createTestFile(100L);
+ createTestFile(/* lastPullTimeMillis= */ 100L);
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
// no exception should be thrown, storage and pull time should match
- assertEquals(
- 100L, mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis);
- assertEquals(
- 100L, mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertProtoArrayEquals(mVoiceCallRatUsages, voiceCallRatUsage);
- assertProtoArrayEquals(mVoiceCallSessions, voiceCallSession);
+ assertAllPullTimestampEquals(100L);
+ assertProtoArrayEquals(mVoiceCallRatUsages, mPersistAtomsStorage.getVoiceCallRatUsages(0L));
+ assertProtoArrayEquals(mVoiceCallSessions, mPersistAtomsStorage.getVoiceCallSessions(0L));
+ assertProtoArrayEqualsIgnoringOrder(
+ mServiceStates, mPersistAtomsStorage.getCellularServiceStates(0L));
+ assertProtoArrayEqualsIgnoringOrder(
+ mServiceSwitches, mPersistAtomsStorage.getCellularDataServiceSwitches(0L));
}
@Test
@@ -464,46 +602,31 @@
mPersistAtomsStorage.incTimeMillis(100L);
// call should be added successfully, there should be no RAT usage, changes should be saved
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
+ verifyCurrentStateSavedToFileOnce();
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getVoiceCallRatUsages(0L));
VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertNotNull(voiceCallRatUsage);
- assertEquals(0, voiceCallRatUsage.length);
assertProtoArrayEquals(new VoiceCallSession[] {mCall1Proto}, voiceCallSession);
- InOrder inOrder = inOrder(mTestFileOutputStream);
- inOrder.verify(mTestFileOutputStream, times(1))
- .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
- inOrder.verify(mTestFileOutputStream, times(1)).close();
- inOrder.verifyNoMoreInteractions();
}
@Test
@SmallTest
public void addVoiceCallSession_withExistingCalls() throws Exception {
- createTestFile(100L);
+ createTestFile(START_TIME_MILLIS);
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
mPersistAtomsStorage.addVoiceCallSession(mCall1Proto);
mPersistAtomsStorage.incTimeMillis(100L);
// call should be added successfully, RAT usages should not change, changes should be saved
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertNotNull(voiceCallRatUsage);
- assertEquals(mVoiceCallRatUsages.length, voiceCallRatUsage.length);
- assertNotNull(voiceCallSession);
- // call lists are randomized, but sorted version should be identical
+ assertProtoArrayEquals(mVoiceCallRatUsages, mPersistAtomsStorage.getVoiceCallRatUsages(0L));
VoiceCallSession[] expectedVoiceCallSessions =
new VoiceCallSession[] {
mCall1Proto, mCall1Proto, mCall2Proto, mCall3Proto, mCall4Proto
};
- Arrays.sort(expectedVoiceCallSessions, sProtoComparator);
- Arrays.sort(voiceCallSession, sProtoComparator);
- assertProtoArrayEquals(expectedVoiceCallSessions, voiceCallSession);
- InOrder inOrder = inOrder(mTestFileOutputStream);
- inOrder.verify(mTestFileOutputStream, times(1))
- .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
- inOrder.verify(mTestFileOutputStream, times(1)).close();
- inOrder.verifyNoMoreInteractions();
+ // call list is randomized at this point
+ verifyCurrentStateSavedToFileOnce();
+ assertProtoArrayEqualsIgnoringOrder(
+ expectedVoiceCallSessions, mPersistAtomsStorage.getVoiceCallSessions(0L));
}
@Test
@@ -517,9 +640,10 @@
mPersistAtomsStorage.incTimeMillis(100L);
// one previous call should be evicted, the new call should be added
+ verifyCurrentStateSavedToFileOnce();
VoiceCallSession[] calls = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertHasCall(calls, mCall1Proto, 49);
- assertHasCall(calls, mCall2Proto, 1);
+ assertHasCall(calls, mCall1Proto, /* expectedCount= */ 49);
+ assertHasCall(calls, mCall2Proto, /* expectedCount= */ 1);
}
@Test
@@ -533,25 +657,16 @@
mPersistAtomsStorage.incTimeMillis(100L);
// RAT should be added successfully, calls should not change, changes should be saved
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- RawVoiceCallRatUsage[] expectedVoiceCallRatUsage = mVoiceCallRatUsages.clone();
- Arrays.sort(expectedVoiceCallRatUsage, sProtoComparator);
- Arrays.sort(voiceCallRatUsage, sProtoComparator);
- assertProtoArrayEquals(expectedVoiceCallRatUsage, voiceCallRatUsage);
- assertNotNull(voiceCallSession);
- assertEquals(0, voiceCallSession.length);
- InOrder inOrder = inOrder(mTestFileOutputStream);
- inOrder.verify(mTestFileOutputStream, times(1))
- .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
- inOrder.verify(mTestFileOutputStream, times(1)).close();
- inOrder.verifyNoMoreInteractions();
+ verifyCurrentStateSavedToFileOnce();
+ assertProtoArrayEqualsIgnoringOrder(
+ mVoiceCallRatUsages, mPersistAtomsStorage.getVoiceCallRatUsages(0L));
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getVoiceCallSessions(0L));
}
@Test
@SmallTest
public void addVoiceCallRatUsage_withExistingUsages() throws Exception {
- createTestFile(100L);
+ createTestFile(START_TIME_MILLIS);
VoiceCallRatTracker ratTracker = VoiceCallRatTracker.fromProto(mVoiceCallRatUsages);
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
@@ -559,42 +674,30 @@
mPersistAtomsStorage.incTimeMillis(100L);
// RAT should be added successfully, calls should not change, changes should be saved
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
// call count and duration should become doubled since mVoiceCallRatUsages applied through
// both file and addVoiceCallRatUsage()
- RawVoiceCallRatUsage[] expectedVoiceCallRatUsage =
+ verifyCurrentStateSavedToFileOnce();
+ VoiceCallRatUsage[] expectedVoiceCallRatUsage =
multiplyVoiceCallRatUsage(mVoiceCallRatUsages, 2);
- Arrays.sort(expectedVoiceCallRatUsage, sProtoComparator);
- Arrays.sort(voiceCallRatUsage, sProtoComparator);
- assertProtoArrayEquals(expectedVoiceCallRatUsage, voiceCallRatUsage);
- assertNotNull(voiceCallSession);
- assertEquals(mVoiceCallSessions.length, voiceCallSession.length);
- InOrder inOrder = inOrder(mTestFileOutputStream);
- inOrder.verify(mTestFileOutputStream, times(1))
- .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
- inOrder.verify(mTestFileOutputStream, times(1)).close();
- inOrder.verifyNoMoreInteractions();
+ assertProtoArrayEqualsIgnoringOrder(
+ expectedVoiceCallRatUsage, mPersistAtomsStorage.getVoiceCallRatUsages(0L));
+ assertProtoArrayEquals(mVoiceCallSessions, mPersistAtomsStorage.getVoiceCallSessions(0L));
}
@Test
@SmallTest
public void addVoiceCallRatUsage_empty() throws Exception {
createEmptyTestFile();
- VoiceCallRatTracker ratTracker = VoiceCallRatTracker.fromProto(new RawVoiceCallRatUsage[0]);
+ VoiceCallRatTracker ratTracker = VoiceCallRatTracker.fromProto(new VoiceCallRatUsage[0]);
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
mPersistAtomsStorage.addVoiceCallRatUsage(ratTracker);
mPersistAtomsStorage.incTimeMillis(100L);
// RAT should be added successfully, calls should not change
- // in this case it does not necessarily need to save
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(0L);
- VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(0L);
- assertNotNull(voiceCallRatUsage);
- assertEquals(0, voiceCallRatUsage.length);
- assertNotNull(voiceCallSession);
- assertEquals(0, voiceCallSession.length);
+ // in this case saving is unnecessarily
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getVoiceCallRatUsages(0L));
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getVoiceCallSessions(0L));
}
@Test
@@ -604,7 +707,7 @@
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(100L);
+ VoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(100L);
// should be denied
assertNull(voiceCallRatUsage);
@@ -617,9 +720,9 @@
mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
mPersistAtomsStorage.incTimeMillis(100L);
- RawVoiceCallRatUsage[] voiceCallRatUsage1 = mPersistAtomsStorage.getVoiceCallRatUsages(50L);
+ VoiceCallRatUsage[] voiceCallRatUsage1 = mPersistAtomsStorage.getVoiceCallRatUsages(50L);
mPersistAtomsStorage.incTimeMillis(100L);
- RawVoiceCallRatUsage[] voiceCallRatUsage2 = mPersistAtomsStorage.getVoiceCallRatUsages(50L);
+ VoiceCallRatUsage[] voiceCallRatUsage2 = mPersistAtomsStorage.getVoiceCallRatUsages(50L);
long voiceCallSessionPullTimestampMillis =
mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis;
VoiceCallSession[] voiceCallSession = mPersistAtomsStorage.getVoiceCallSessions(50L);
@@ -627,19 +730,19 @@
// first set of results should equal to file contents, second should be empty, corresponding
// pull timestamp should be updated and saved, other fields should be unaffected
assertProtoArrayEquals(mVoiceCallRatUsages, voiceCallRatUsage1);
- assertProtoArrayEquals(new RawVoiceCallRatUsage[0], voiceCallRatUsage2);
+ assertProtoArrayIsEmpty(voiceCallRatUsage2);
assertEquals(
START_TIME_MILLIS + 200L,
- mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis);
+ mPersistAtomsStorage.getAtomsProto().voiceCallRatUsagePullTimestampMillis);
assertProtoArrayEquals(mVoiceCallSessions, voiceCallSession);
assertEquals(START_TIME_MILLIS, voiceCallSessionPullTimestampMillis);
InOrder inOrder = inOrder(mTestFileOutputStream);
assertEquals(
START_TIME_MILLIS + 100L,
- getAtomsWritten(inOrder).rawVoiceCallRatUsagePullTimestampMillis);
+ getAtomsWritten(inOrder).voiceCallRatUsagePullTimestampMillis);
assertEquals(
START_TIME_MILLIS + 200L,
- getAtomsWritten(inOrder).rawVoiceCallRatUsagePullTimestampMillis);
+ getAtomsWritten(inOrder).voiceCallRatUsagePullTimestampMillis);
assertEquals(
START_TIME_MILLIS + 200L,
getAtomsWritten(inOrder).voiceCallSessionPullTimestampMillis);
@@ -670,13 +773,13 @@
mPersistAtomsStorage.incTimeMillis(100L);
VoiceCallSession[] voiceCallSession2 = mPersistAtomsStorage.getVoiceCallSessions(50L);
long voiceCallRatUsagePullTimestampMillis =
- mPersistAtomsStorage.getAtomsProto().rawVoiceCallRatUsagePullTimestampMillis;
- RawVoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(50L);
+ mPersistAtomsStorage.getAtomsProto().voiceCallRatUsagePullTimestampMillis;
+ VoiceCallRatUsage[] voiceCallRatUsage = mPersistAtomsStorage.getVoiceCallRatUsages(50L);
// first set of results should equal to file contents, second should be empty, corresponding
// pull timestamp should be updated and saved, other fields should be unaffected
assertProtoArrayEquals(mVoiceCallSessions, voiceCallSession1);
- assertProtoArrayEquals(new VoiceCallSession[0], voiceCallSession2);
+ assertProtoArrayIsEmpty(voiceCallSession2);
assertEquals(
START_TIME_MILLIS + 200L,
mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
@@ -691,10 +794,486 @@
getAtomsWritten(inOrder).voiceCallSessionPullTimestampMillis);
assertEquals(
START_TIME_MILLIS + 200L,
- getAtomsWritten(inOrder).rawVoiceCallRatUsagePullTimestampMillis);
+ getAtomsWritten(inOrder).voiceCallRatUsagePullTimestampMillis);
inOrder.verifyNoMoreInteractions();
}
+ @Test
+ @SmallTest
+ public void addCellularServiceStateAndCellularDataServiceSwitch_emptyProto() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addCellularServiceStateAndCellularDataServiceSwitch(
+ mServiceState1Proto, mServiceSwitch1Proto);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ CellularServiceState[] serviceStates = mPersistAtomsStorage.getCellularServiceStates(0L);
+ CellularDataServiceSwitch[] serviceSwitches =
+ mPersistAtomsStorage.getCellularDataServiceSwitches(0L);
+ assertProtoArrayEquals(new CellularServiceState[] {mServiceState1Proto}, serviceStates);
+ assertProtoArrayEquals(
+ new CellularDataServiceSwitch[] {mServiceSwitch1Proto}, serviceSwitches);
+ }
+
+ @Test
+ @SmallTest
+ public void addCellularServiceStateAndCellularDataServiceSwitch_withExistingEntries()
+ throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addCellularServiceStateAndCellularDataServiceSwitch(
+ mServiceState1Proto, mServiceSwitch1Proto);
+
+ mPersistAtomsStorage.addCellularServiceStateAndCellularDataServiceSwitch(
+ mServiceState2Proto, mServiceSwitch2Proto);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ CellularServiceState[] serviceStates = mPersistAtomsStorage.getCellularServiceStates(0L);
+ CellularDataServiceSwitch[] serviceSwitches =
+ mPersistAtomsStorage.getCellularDataServiceSwitches(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new CellularServiceState[] {mServiceState1Proto, mServiceState2Proto},
+ serviceStates);
+ assertProtoArrayEqualsIgnoringOrder(
+ new CellularDataServiceSwitch[] {mServiceSwitch1Proto, mServiceSwitch2Proto},
+ serviceSwitches);
+ }
+
+ @Test
+ @SmallTest
+ public void addCellularServiceStateAndCellularDataServiceSwitch_updateExistingEntries()
+ throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ CellularServiceState newServiceState1Proto = copyOf(mServiceState1Proto);
+ CellularDataServiceSwitch newServiceSwitch1Proto = copyOf(mServiceSwitch1Proto);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ mPersistAtomsStorage.addCellularServiceStateAndCellularDataServiceSwitch(
+ copyOf(mServiceState1Proto), copyOf(mServiceSwitch1Proto));
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // mServiceState1Proto's duration and mServiceSwitch1Proto's switch should be doubled
+ verifyCurrentStateSavedToFileOnce();
+ CellularServiceState[] serviceStates = mPersistAtomsStorage.getCellularServiceStates(0L);
+ newServiceState1Proto.totalTimeMillis *= 2;
+ assertProtoArrayEqualsIgnoringOrder(
+ new CellularServiceState[] {
+ newServiceState1Proto,
+ mServiceState2Proto,
+ mServiceState3Proto,
+ mServiceState4Proto
+ },
+ serviceStates);
+ CellularDataServiceSwitch[] serviceSwitches =
+ mPersistAtomsStorage.getCellularDataServiceSwitches(0L);
+ newServiceSwitch1Proto.switchCount *= 2;
+ assertProtoArrayEqualsIgnoringOrder(
+ new CellularDataServiceSwitch[] {newServiceSwitch1Proto, mServiceSwitch2Proto},
+ serviceSwitches);
+ }
+
+ @Test
+ @SmallTest
+ public void addCellularServiceStateAndCellularDataServiceSwitch_tooManyServiceStates()
+ throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ Queue<CellularServiceState> expectedServiceStates = new LinkedList<>();
+ Queue<CellularDataServiceSwitch> expectedServiceSwitches = new LinkedList<>();
+
+ // Add 51 service states, with the first being least recent
+ for (int i = 0; i < 51; i++) {
+ CellularServiceState state = new CellularServiceState();
+ state.voiceRat = i / 10;
+ state.dataRat = i % 10;
+ expectedServiceStates.add(state);
+ CellularDataServiceSwitch serviceSwitch = new CellularDataServiceSwitch();
+ serviceSwitch.ratFrom = i / 10;
+ serviceSwitch.ratTo = i % 10;
+ expectedServiceSwitches.add(serviceSwitch);
+ mPersistAtomsStorage.addCellularServiceStateAndCellularDataServiceSwitch(
+ copyOf(state), copyOf(serviceSwitch));
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+
+ // The least recent (the first) service state should be evicted
+ verifyCurrentStateSavedToFileOnce();
+ CellularServiceState[] serviceStates = mPersistAtomsStorage.getCellularServiceStates(0L);
+ expectedServiceStates.remove();
+ assertProtoArrayEqualsIgnoringOrder(
+ expectedServiceStates.toArray(new CellularServiceState[0]), serviceStates);
+ CellularDataServiceSwitch[] serviceSwitches =
+ mPersistAtomsStorage.getCellularDataServiceSwitches(0L);
+ expectedServiceSwitches.remove();
+ assertProtoArrayEqualsIgnoringOrder(
+ expectedServiceSwitches.toArray(new CellularDataServiceSwitch[0]), serviceSwitches);
+ }
+
+ @Test
+ @SmallTest
+ public void getCellularDataServiceSwitches_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ CellularDataServiceSwitch[] serviceSwitches =
+ mPersistAtomsStorage.getCellularDataServiceSwitches(100L);
+
+ // should be denied
+ assertNull(serviceSwitches);
+ }
+
+ @Test
+ @SmallTest
+ public void getCellularDataServiceSwitches_withSavedAtoms() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ CellularDataServiceSwitch[] serviceSwitches1 =
+ mPersistAtomsStorage.getCellularDataServiceSwitches(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ CellularDataServiceSwitch[] serviceSwitches2 =
+ mPersistAtomsStorage.getCellularDataServiceSwitches(50L);
+
+ // first set of results should equal to file contents, second should be empty, corresponding
+ // pull timestamp should be updated and saved
+ assertProtoArrayEqualsIgnoringOrder(
+ new CellularDataServiceSwitch[] {mServiceSwitch1Proto, mServiceSwitch2Proto},
+ serviceSwitches1);
+ assertProtoArrayEquals(new CellularDataServiceSwitch[0], serviceSwitches2);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ mPersistAtomsStorage.getAtomsProto().cellularDataServiceSwitchPullTimestampMillis);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(
+ START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).cellularDataServiceSwitchPullTimestampMillis);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).cellularDataServiceSwitchPullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void getCellularServiceStates_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ CellularServiceState[] serviceStates = mPersistAtomsStorage.getCellularServiceStates(100L);
+
+ // should be denied
+ assertNull(serviceStates);
+ }
+
+ @Test
+ @SmallTest
+ public void getCellularServiceStates_withSavedAtoms() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ CellularServiceState[] serviceStates1 = mPersistAtomsStorage.getCellularServiceStates(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ CellularServiceState[] serviceStates2 = mPersistAtomsStorage.getCellularServiceStates(50L);
+
+ // first set of results should equal to file contents, second should be empty, corresponding
+ // pull timestamp should be updated and saved
+ assertProtoArrayEqualsIgnoringOrder(
+ new CellularServiceState[] {
+ mServiceState1Proto,
+ mServiceState2Proto,
+ mServiceState3Proto,
+ mServiceState4Proto
+ },
+ serviceStates1);
+ assertProtoArrayEquals(new CellularServiceState[0], serviceStates2);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ mPersistAtomsStorage.getAtomsProto().cellularServiceStatePullTimestampMillis);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(
+ START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).cellularServiceStatePullTimestampMillis);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).cellularServiceStatePullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationStats_emptyProto() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addImsRegistrationStats(mImsRegistrationStatsLte0);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationStats[] regStats = mPersistAtomsStorage.getImsRegistrationStats(0L);
+ assertProtoArrayEquals(new ImsRegistrationStats[] {mImsRegistrationStatsLte0}, regStats);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationStats_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addImsRegistrationStats(mImsRegistrationStatsLte0);
+
+ mPersistAtomsStorage.addImsRegistrationStats(mImsRegistrationStatsWifi0);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationStats[] regStats = mPersistAtomsStorage.getImsRegistrationStats(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationStats[] {mImsRegistrationStatsLte0, mImsRegistrationStatsWifi0},
+ regStats);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationStats_updateExistingEntries() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ ImsRegistrationStats newImsRegistrationStatsLte0 = copyOf(mImsRegistrationStatsLte0);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ mPersistAtomsStorage.addImsRegistrationStats(copyOf(mImsRegistrationStatsLte0));
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // mImsRegistrationStatsLte0's durations should be doubled
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationStats[] serviceStates = mPersistAtomsStorage.getImsRegistrationStats(0L);
+ newImsRegistrationStatsLte0.registeredMillis *= 2;
+ newImsRegistrationStatsLte0.voiceCapableMillis *= 2;
+ newImsRegistrationStatsLte0.voiceAvailableMillis *= 2;
+ newImsRegistrationStatsLte0.smsCapableMillis *= 2;
+ newImsRegistrationStatsLte0.smsAvailableMillis *= 2;
+ newImsRegistrationStatsLte0.videoCapableMillis *= 2;
+ newImsRegistrationStatsLte0.videoAvailableMillis *= 2;
+ newImsRegistrationStatsLte0.utCapableMillis *= 2;
+ newImsRegistrationStatsLte0.utAvailableMillis *= 2;
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationStats[] {
+ newImsRegistrationStatsLte0,
+ mImsRegistrationStatsWifi0,
+ mImsRegistrationStatsLte1
+ },
+ serviceStates);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationStats_tooManyRegistrationStats() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ Queue<ImsRegistrationStats> expectedRegistrationStats = new LinkedList<>();
+
+ // Add 11 registration stats
+ for (int i = 0; i < 11; i++) {
+ ImsRegistrationStats stats = copyOf(mImsRegistrationStatsLte0);
+ stats.rat = i;
+ expectedRegistrationStats.add(stats);
+ mPersistAtomsStorage.addImsRegistrationStats(stats);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+
+ // The least recent (the first) registration stats should be evicted
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationStats[] stats = mPersistAtomsStorage.getImsRegistrationStats(0L);
+ expectedRegistrationStats.remove();
+ assertProtoArrayEqualsIgnoringOrder(
+ expectedRegistrationStats.toArray(new ImsRegistrationStats[0]), stats);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationTermination_emptyProto() throws Exception {
+ createEmptyTestFile();
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addImsRegistrationTermination(mImsRegistrationTerminationLte);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationTermination[] terminations =
+ mPersistAtomsStorage.getImsRegistrationTerminations(0L);
+ assertProtoArrayEquals(
+ new ImsRegistrationTermination[] {mImsRegistrationTerminationLte}, terminations);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationTermination_withExistingEntries() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.addImsRegistrationTermination(mImsRegistrationTerminationLte);
+
+ mPersistAtomsStorage.addImsRegistrationTermination(mImsRegistrationTerminationWifi);
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // service state and service switch should be added successfully
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationTermination[] terminations =
+ mPersistAtomsStorage.getImsRegistrationTerminations(0L);
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationTermination[] {
+ mImsRegistrationTerminationLte, mImsRegistrationTerminationWifi
+ },
+ terminations);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationTermination_updateExistingEntries() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+ ImsRegistrationTermination newTermination = copyOf(mImsRegistrationTerminationWifi);
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+
+ mPersistAtomsStorage.addImsRegistrationTermination(copyOf(mImsRegistrationTerminationWifi));
+ mPersistAtomsStorage.incTimeMillis(100L);
+
+ // mImsRegistrationTerminationWifi's count should be doubled
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationTermination[] terminations =
+ mPersistAtomsStorage.getImsRegistrationTerminations(0L);
+ newTermination.count *= 2;
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationTermination[] {mImsRegistrationTerminationLte, newTermination},
+ terminations);
+ }
+
+ @Test
+ @SmallTest
+ public void addImsRegistrationTermination_tooManyTerminations() throws Exception {
+ createEmptyTestFile();
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ Queue<ImsRegistrationTermination> expectedTerminations = new LinkedList<>();
+
+ // Add 11 registration terminations
+ for (int i = 0; i < 11; i++) {
+ ImsRegistrationTermination termination = copyOf(mImsRegistrationTerminationLte);
+ termination.reasonCode = i;
+ expectedTerminations.add(termination);
+ mPersistAtomsStorage.addImsRegistrationTermination(termination);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ }
+
+ // The least recent (the first) registration termination should be evicted
+ verifyCurrentStateSavedToFileOnce();
+ ImsRegistrationTermination[] terminations =
+ mPersistAtomsStorage.getImsRegistrationTerminations(0L);
+ expectedTerminations.remove();
+ assertProtoArrayEqualsIgnoringOrder(
+ expectedTerminations.toArray(new ImsRegistrationTermination[0]), terminations);
+ }
+
+ @Test
+ @SmallTest
+ public void getImsRegistrationStats_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ ImsRegistrationStats[] stats = mPersistAtomsStorage.getImsRegistrationStats(100L);
+
+ // should be denied
+ assertNull(stats);
+ }
+
+ @Test
+ @SmallTest
+ public void getImsRegistrationStats_withSavedAtoms() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ ImsRegistrationStats[] stats1 = mPersistAtomsStorage.getImsRegistrationStats(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ ImsRegistrationStats[] stats2 = mPersistAtomsStorage.getImsRegistrationStats(50L);
+
+ // first set of results should equal to file contents, second should be empty, corresponding
+ // pull timestamp should be updated and saved
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationStats[] {
+ mImsRegistrationStatsLte0, mImsRegistrationStatsWifi0, mImsRegistrationStatsLte1
+ },
+ stats1);
+ assertProtoArrayEquals(new ImsRegistrationStats[0], stats2);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ mPersistAtomsStorage.getAtomsProto().imsRegistrationStatsPullTimestampMillis);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(
+ START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).imsRegistrationStatsPullTimestampMillis);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).imsRegistrationStatsPullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SmallTest
+ public void getImsRegistrationTerminations_tooFrequent() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(50L); // pull interval less than minimum
+ ImsRegistrationTermination[] terminations =
+ mPersistAtomsStorage.getImsRegistrationTerminations(100L);
+
+ // should be denied
+ assertNull(terminations);
+ }
+
+ @Test
+ @SmallTest
+ public void getImsRegistrationTerminations_withSavedAtoms() throws Exception {
+ createTestFile(START_TIME_MILLIS);
+
+ mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ ImsRegistrationTermination[] terminations1 =
+ mPersistAtomsStorage.getImsRegistrationTerminations(50L);
+ mPersistAtomsStorage.incTimeMillis(100L);
+ ImsRegistrationTermination[] terminations2 =
+ mPersistAtomsStorage.getImsRegistrationTerminations(50L);
+
+ // first set of results should equal to file contents, second should be empty, corresponding
+ // pull timestamp should be updated and saved
+ assertProtoArrayEqualsIgnoringOrder(
+ new ImsRegistrationTermination[] {
+ mImsRegistrationTerminationLte, mImsRegistrationTerminationWifi
+ },
+ terminations1);
+ assertProtoArrayEquals(new ImsRegistrationTermination[0], terminations2);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ mPersistAtomsStorage.getAtomsProto().imsRegistrationTerminationPullTimestampMillis);
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ assertEquals(
+ START_TIME_MILLIS + 100L,
+ getAtomsWritten(inOrder).imsRegistrationTerminationPullTimestampMillis);
+ assertEquals(
+ START_TIME_MILLIS + 200L,
+ getAtomsWritten(inOrder).imsRegistrationTerminationPullTimestampMillis);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ /* Utilities */
+
private void createEmptyTestFile() throws Exception {
PersistAtoms atoms = new PersistAtoms();
FileOutputStream stream = new FileOutputStream(mTestFile);
@@ -704,10 +1283,18 @@
private void createTestFile(long lastPullTimeMillis) throws Exception {
PersistAtoms atoms = new PersistAtoms();
- atoms.rawVoiceCallRatUsagePullTimestampMillis = lastPullTimeMillis;
+ atoms.voiceCallRatUsagePullTimestampMillis = lastPullTimeMillis;
+ atoms.voiceCallRatUsage = mVoiceCallRatUsages;
atoms.voiceCallSessionPullTimestampMillis = lastPullTimeMillis;
- atoms.rawVoiceCallRatUsage = mVoiceCallRatUsages;
atoms.voiceCallSession = mVoiceCallSessions;
+ atoms.cellularServiceStatePullTimestampMillis = lastPullTimeMillis;
+ atoms.cellularServiceState = mServiceStates;
+ atoms.cellularDataServiceSwitchPullTimestampMillis = lastPullTimeMillis;
+ atoms.cellularDataServiceSwitch = mServiceSwitches;
+ atoms.imsRegistrationStatsPullTimestampMillis = lastPullTimeMillis;
+ atoms.imsRegistrationStats = mImsRegistrationStats;
+ atoms.imsRegistrationTerminationPullTimestampMillis = lastPullTimeMillis;
+ atoms.imsRegistrationTermination = mImsRegistrationTerminations;
FileOutputStream stream = new FileOutputStream(mTestFile);
stream.write(PersistAtoms.toByteArray(atoms));
stream.close();
@@ -731,11 +1318,11 @@
}
}
- private static RawVoiceCallRatUsage[] multiplyVoiceCallRatUsage(
- RawVoiceCallRatUsage[] usages, int times) {
- RawVoiceCallRatUsage[] multipliedUsages = new RawVoiceCallRatUsage[usages.length];
+ private static VoiceCallRatUsage[] multiplyVoiceCallRatUsage(
+ VoiceCallRatUsage[] usages, int times) {
+ VoiceCallRatUsage[] multipliedUsages = new VoiceCallRatUsage[usages.length];
for (int i = 0; i < usages.length; i++) {
- multipliedUsages[i] = new RawVoiceCallRatUsage();
+ multipliedUsages[i] = new VoiceCallRatUsage();
multipliedUsages[i].carrierId = usages[i].carrierId;
multipliedUsages[i].rat = usages[i].rat;
multipliedUsages[i].callCount = usages[i].callCount * 2;
@@ -744,19 +1331,73 @@
return multipliedUsages;
}
+ private static CellularServiceState copyOf(CellularServiceState source) throws Exception {
+ return CellularServiceState.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static CellularDataServiceSwitch copyOf(CellularDataServiceSwitch source)
+ throws Exception {
+ return CellularDataServiceSwitch.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static ImsRegistrationStats copyOf(ImsRegistrationStats source) throws Exception {
+ return ImsRegistrationStats.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private static ImsRegistrationTermination copyOf(ImsRegistrationTermination source)
+ throws Exception {
+ return ImsRegistrationTermination.parseFrom(MessageNano.toByteArray(source));
+ }
+
+ private void assertAllPullTimestampEquals(long timestamp) {
+ assertEquals(
+ timestamp,
+ mPersistAtomsStorage.getAtomsProto().voiceCallRatUsagePullTimestampMillis);
+ assertEquals(
+ timestamp,
+ mPersistAtomsStorage.getAtomsProto().voiceCallSessionPullTimestampMillis);
+ assertEquals(
+ timestamp,
+ mPersistAtomsStorage.getAtomsProto().cellularServiceStatePullTimestampMillis);
+ assertEquals(
+ timestamp,
+ mPersistAtomsStorage.getAtomsProto().cellularDataServiceSwitchPullTimestampMillis);
+ }
+
+ private void assertStorageIsEmptyForAllAtoms() {
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getVoiceCallRatUsages(0L));
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getVoiceCallSessions(0L));
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getCellularServiceStates(0L));
+ assertProtoArrayIsEmpty(mPersistAtomsStorage.getCellularDataServiceSwitches(0L));
+ }
+
+ private static <T extends MessageNano> void assertProtoArrayIsEmpty(T[] array) {
+ assertNotNull(array);
+ assertEquals(0, array.length);
+ }
+
private static void assertProtoArrayEquals(MessageNano[] expected, MessageNano[] actual) {
assertNotNull(expected);
assertNotNull(actual);
- assertEquals(expected.length, actual.length);
+ String message =
+ "Expected:\n" + Arrays.toString(expected) + "\nGot:\n" + Arrays.toString(actual);
+ assertEquals(message, expected.length, actual.length);
for (int i = 0; i < expected.length; i++) {
- assertTrue(
- String.format(
- "Message %d of %d differs:\n=== expected ===\n%s=== got ===\n%s",
- i + 1, expected.length, expected[i].toString(), actual[i].toString()),
- MessageNano.messageNanoEquals(expected[i], actual[i]));
+ assertTrue(message, MessageNano.messageNanoEquals(expected[i], actual[i]));
}
}
+ private static void assertProtoArrayEqualsIgnoringOrder(
+ MessageNano[] expected, MessageNano[] actual) {
+ assertNotNull(expected);
+ assertNotNull(actual);
+ expected = expected.clone();
+ actual = actual.clone();
+ Arrays.sort(expected, sProtoComparator);
+ Arrays.sort(actual, sProtoComparator);
+ assertProtoArrayEquals(expected, actual);
+ }
+
private static void assertHasCall(
VoiceCallSession[] calls, @Nullable VoiceCallSession expectedCall, int expectedCount) {
assertNotNull(calls);
@@ -770,4 +1411,12 @@
}
assertEquals(expectedCount, actualCount);
}
+
+ private void verifyCurrentStateSavedToFileOnce() throws Exception {
+ InOrder inOrder = inOrder(mTestFileOutputStream);
+ inOrder.verify(mTestFileOutputStream, times(1))
+ .write(eq(PersistAtoms.toByteArray(mPersistAtomsStorage.getAtomsProto())));
+ inOrder.verify(mTestFileOutputStream, times(1)).close();
+ inOrder.verifyNoMoreInteractions();
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
new file mode 100644
index 0000000..ca967ab
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/ServiceStateStatsTest.java
@@ -0,0 +1,840 @@
+/*
+ * Copyright (C) 2020 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.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.telephony.AccessNetworkConstants;
+import android.telephony.Annotation.NetworkType;
+import android.telephony.NetworkRegistrationInfo;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.Phone;
+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.uicc.IccCardStatus.CardState;
+import com.android.internal.telephony.uicc.UiccSlot;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+public class ServiceStateStatsTest extends TelephonyTest {
+ private static final long START_TIME_MILLIS = 2000L;
+ private static final int CARRIER1_ID = 1;
+ private static final int CARRIER2_ID = 1187;
+
+ @Mock private UiccSlot mPhysicalSlot0;
+ @Mock private UiccSlot mPhysicalSlot1;
+ @Mock private Phone mSecondPhone;
+
+ private TestableServiceStateStats mServiceStateStats;
+
+ private static class TestableServiceStateStats extends ServiceStateStats {
+ private long mTimeMillis = START_TIME_MILLIS;
+
+ TestableServiceStateStats(Phone phone) {
+ super(phone);
+ }
+
+ @Override
+ protected long getTimeMillis() {
+ // 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)
+ return mTimeMillis == 0L ? START_TIME_MILLIS : mTimeMillis;
+ }
+
+ private void setTimeMillis(long timeMillis) {
+ mTimeMillis = timeMillis;
+ }
+
+ private void incTimeMillis(long timeMillis) {
+ mTimeMillis += timeMillis;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp(getClass().getSimpleName());
+
+ doReturn(CARRIER1_ID).when(mPhone).getCarrierId();
+ doReturn(mImsPhone).when(mPhone).getImsPhone();
+
+ // Single physical SIM
+ doReturn(true).when(mPhysicalSlot0).isActive();
+ doReturn(CardState.CARDSTATE_PRESENT).when(mPhysicalSlot0).getCardState();
+ doReturn(false).when(mPhysicalSlot0).isEuicc();
+ doReturn(new UiccSlot[] {mPhysicalSlot0}).when(mUiccController).getUiccSlots();
+ doReturn(mPhysicalSlot0).when(mUiccController).getUiccSlot(0);
+ doReturn(mPhysicalSlot0).when(mUiccController).getUiccSlotForPhone(0);
+
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_LTE);
+
+ mServiceStateStats = new TestableServiceStateStats(mPhone);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_inService() throws Exception {
+ // Using default service state for LTE
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+
+ // Duration should be counted and there should not be any switch
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage)
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getValue();
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_outOfService() throws Exception {
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getDataRegState();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+
+ // Duration should be counted and there should not be any switch
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage)
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getValue();
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_airplaneMode() throws Exception {
+ doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getDataRegState();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ doReturn(-1).when(mPhone).getCarrierId();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+
+ // There should be no new switches, service states, or added durations
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_airplaneModeWithWifiCalling() throws Exception {
+ doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getDataRegState();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_IWLAN).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ doReturn(true).when(mImsPhone).isWifiCallingEnabled();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+
+ // Duration for Wifi calling should be counted and there should not be any switch
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage)
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getValue();
+ assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_noSimCardEmergencyOnly() throws Exception {
+ // Using default service state for LTE
+ doReturn(CardState.CARDSTATE_ABSENT).when(mPhysicalSlot0).getCardState();
+ doReturn(ServiceState.STATE_EMERGENCY_ONLY).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_EMERGENCY_ONLY).when(mServiceState).getDataRegState();
+ doReturn(-1).when(mPhone).getCarrierId();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+
+ // Duration should be counted and there should not be any switch
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage)
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getValue();
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(-1, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_noSimCardOutOfService() throws Exception {
+ doReturn(CardState.CARDSTATE_ABSENT).when(mPhysicalSlot0).getCardState();
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_OUT_OF_SERVICE).when(mServiceState).getDataRegState();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ doReturn(-1).when(mPhone).getCarrierId();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+
+ // Duration should be counted and there should not be any switch
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage)
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getValue();
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(-1, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void conclude_longOnGoingServiceState() throws Exception {
+ // Using default service state for LTE
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.conclude();
+
+ // There should be 2 separate service state updates, which should be different objects
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage, times(2))
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ assertNotSame(captor.getAllValues().get(0), captor.getAllValues().get(1));
+ CellularServiceState state = captor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = captor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_sameRats() throws Exception {
+ // Using default service state for LTE
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ // Should produce 1 service state atom with LTE and no data service switch
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage)
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getValue();
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_differentDataRats() throws Exception {
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_LTE);
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+
+ // There should be 2 service states and a data service switch
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> serviceStateCaptor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ ArgumentCaptor<CellularDataServiceSwitch> serviceSwitchCaptor =
+ ArgumentCaptor.forClass(CellularDataServiceSwitch.class);
+ verify(mPersistAtomsStorage, times(2))
+ .addCellularServiceStateAndCellularDataServiceSwitch(
+ serviceStateCaptor.capture(), serviceSwitchCaptor.capture());
+ CellularServiceState state = serviceStateCaptor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = serviceStateCaptor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ CellularDataServiceSwitch serviceSwitch = serviceSwitchCaptor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, serviceSwitch.ratFrom);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, serviceSwitch.ratTo);
+ assertEquals(0, serviceSwitch.simSlotIndex);
+ assertFalse(serviceSwitch.isMultiSim);
+ assertEquals(CARRIER1_ID, serviceSwitch.carrierId);
+ assertEquals(1, serviceSwitch.switchCount);
+ assertNull(serviceSwitchCaptor.getAllValues().get(1)); // produced by conclude()
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_differentVoiceRats() throws Exception {
+ // Using default service state for LTE
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ // Voice RAT changes to IWLAN and data RAT stays in LTE according to WWAN PS RAT
+ doReturn(TelephonyManager.NETWORK_TYPE_IWLAN).when(mServiceState).getDataNetworkType();
+ doReturn(true).when(mImsPhone).isWifiCallingEnabled();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+
+ // There should be 2 service states but no data service switch
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage, times(2))
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = captor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_iwlanButNotWifiCalling() throws Exception {
+ // Using default service state for LTE as WWAN PS RAT
+ doReturn(TelephonyManager.NETWORK_TYPE_IWLAN).when(mServiceState).getDataNetworkType();
+ doReturn(false).when(mImsPhone).isWifiCallingEnabled();
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+
+ // Should produce 1 service state atom with voice and data (WWAN PS) RAT as LTE
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage)
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getValue();
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(0L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_endc() throws Exception {
+ // Using default service state for LTE without ENDC
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ // ENDC should stay false
+ doReturn(NetworkRegistrationInfo.NR_STATE_RESTRICTED).when(mServiceState).getNrState();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(200L);
+ // ENDC should become true
+ doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(mServiceState).getNrState();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(400L);
+ // ENDC should stay true
+ doReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED).when(mServiceState).getNrState();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(800L);
+
+ // There should be 4 service state updates (2 distinct service states) but no service switch
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage, times(4))
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = captor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(200L, state.totalTimeMillis);
+ state = captor.getAllValues().get(2);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertTrue(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(400L, state.totalTimeMillis);
+ state = captor.getAllValues().get(3);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertTrue(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(800L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_simSwapSameRat() throws Exception {
+ // Using default service state for LTE
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ // SIM removed, emergency call only
+ doReturn(CardState.CARDSTATE_ABSENT).when(mPhysicalSlot0).getCardState();
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ doReturn(-1).when(mPhone).getCarrierId();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(5000L);
+ // New SIM inserted
+ doReturn(CardState.CARDSTATE_PRESENT).when(mPhysicalSlot0).getCardState();
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(CARRIER2_ID).when(mPhone).getCarrierId();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(200L);
+
+ // There should be 3 service states, but there should be no switches due to carrier change
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage, times(3))
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = captor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(-1, state.carrierId);
+ assertEquals(5000L, state.totalTimeMillis);
+ state = captor.getAllValues().get(2);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER2_ID, state.carrierId);
+ assertEquals(200L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_roaming() throws Exception {
+ // Using default service state for LTE
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ // Voice roaming
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UMTS);
+ doReturn(ServiceState.ROAMING_TYPE_INTERNATIONAL).when(mServiceState).getVoiceRoamingType();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(200L);
+ // Voice and data roaming
+ doReturn(ServiceState.ROAMING_TYPE_INTERNATIONAL).when(mServiceState).getDataRoamingType();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(400L);
+
+ // There should be 3 service states and 1 data service switch (LTE to UMTS)
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> serviceStateCaptor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ ArgumentCaptor<CellularDataServiceSwitch> serviceSwitchCaptor =
+ ArgumentCaptor.forClass(CellularDataServiceSwitch.class);
+ verify(mPersistAtomsStorage, times(3))
+ .addCellularServiceStateAndCellularDataServiceSwitch(
+ serviceStateCaptor.capture(), serviceSwitchCaptor.capture());
+ CellularServiceState state = serviceStateCaptor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = serviceStateCaptor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_INTERNATIONAL, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(200L, state.totalTimeMillis);
+ state = serviceStateCaptor.getAllValues().get(2);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_INTERNATIONAL, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_INTERNATIONAL, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(400L, state.totalTimeMillis);
+ CellularDataServiceSwitch serviceSwitch = serviceSwitchCaptor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, serviceSwitch.ratFrom);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, serviceSwitch.ratTo);
+ assertEquals(0, serviceSwitch.simSlotIndex);
+ assertFalse(serviceSwitch.isMultiSim);
+ assertEquals(CARRIER1_ID, serviceSwitch.carrierId);
+ assertEquals(1, serviceSwitch.switchCount);
+ assertNull(serviceSwitchCaptor.getAllValues().get(1));
+ assertNull(serviceSwitchCaptor.getAllValues().get(2)); // produced by conclude()
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_dualSim() throws Exception {
+ // Using default service state for LTE
+ // Only difference between the 2 slots is slot index
+ mockDualSim(CARRIER1_ID);
+ TestableServiceStateStats mSecondServiceStateStats =
+ new TestableServiceStateStats(mSecondPhone);
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ mSecondServiceStateStats.onServiceStateChanged(mServiceState);
+ mSecondServiceStateStats.incTimeMillis(100L);
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UMTS).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UMTS);
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(200L);
+ mSecondServiceStateStats.onServiceStateChanged(mServiceState);
+ mSecondServiceStateStats.incTimeMillis(200L);
+
+ // There should be 4 service states and 2 data service switches
+ mServiceStateStats.conclude();
+ mSecondServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> serviceStateCaptor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ ArgumentCaptor<CellularDataServiceSwitch> serviceSwitchCaptor =
+ ArgumentCaptor.forClass(CellularDataServiceSwitch.class);
+ verify(mPersistAtomsStorage, times(4))
+ .addCellularServiceStateAndCellularDataServiceSwitch(
+ serviceStateCaptor.capture(), serviceSwitchCaptor.capture());
+ CellularServiceState state = serviceStateCaptor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertTrue(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = serviceStateCaptor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(1, state.simSlotIndex);
+ assertTrue(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = serviceStateCaptor.getAllValues().get(2);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertTrue(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(200L, state.totalTimeMillis);
+ state = serviceStateCaptor.getAllValues().get(3);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(1, state.simSlotIndex);
+ assertTrue(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(200L, state.totalTimeMillis);
+ CellularDataServiceSwitch serviceSwitch = serviceSwitchCaptor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, serviceSwitch.ratFrom);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, serviceSwitch.ratTo);
+ assertEquals(0, serviceSwitch.simSlotIndex);
+ assertTrue(serviceSwitch.isMultiSim);
+ assertEquals(CARRIER1_ID, serviceSwitch.carrierId);
+ assertEquals(1, serviceSwitch.switchCount);
+ serviceSwitch = serviceSwitchCaptor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, serviceSwitch.ratFrom);
+ assertEquals(TelephonyManager.NETWORK_TYPE_UMTS, serviceSwitch.ratTo);
+ assertEquals(1, serviceSwitch.simSlotIndex);
+ assertTrue(serviceSwitch.isMultiSim);
+ assertEquals(CARRIER1_ID, serviceSwitch.carrierId);
+ assertEquals(1, serviceSwitch.switchCount);
+ assertNull(serviceSwitchCaptor.getAllValues().get(2)); // produced by conclude()
+ assertNull(serviceSwitchCaptor.getAllValues().get(3)); // produced by conclude()
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ @Test
+ @SmallTest
+ public void update_airplaneMode() throws Exception {
+ // Using default service state for LTE
+
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(100L);
+ doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_POWER_OFF).when(mServiceState).getDataRegState();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ doReturn(-1).when(mPhone).getCarrierId();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(5000L);
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getVoiceRegState();
+ doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getDataRegState();
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
+ doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getDataNetworkType();
+ mockWwanPsRat(TelephonyManager.NETWORK_TYPE_LTE);
+ doReturn(CARRIER1_ID).when(mPhone).getCarrierId();
+ mServiceStateStats.onServiceStateChanged(mServiceState);
+ mServiceStateStats.incTimeMillis(200L);
+
+ // There should be 2 service state updates (1 distinct service state) and no switches
+ mServiceStateStats.conclude();
+ ArgumentCaptor<CellularServiceState> captor =
+ ArgumentCaptor.forClass(CellularServiceState.class);
+ verify(mPersistAtomsStorage, times(2))
+ .addCellularServiceStateAndCellularDataServiceSwitch(captor.capture(), eq(null));
+ CellularServiceState state = captor.getAllValues().get(0);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(100L, state.totalTimeMillis);
+ state = captor.getAllValues().get(1);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.voiceRat);
+ assertEquals(TelephonyManager.NETWORK_TYPE_LTE, state.dataRat);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.voiceRoamingType);
+ assertEquals(ServiceState.ROAMING_TYPE_NOT_ROAMING, state.dataRoamingType);
+ assertFalse(state.isEndc);
+ assertEquals(0, state.simSlotIndex);
+ assertFalse(state.isMultiSim);
+ assertEquals(CARRIER1_ID, state.carrierId);
+ assertEquals(200L, state.totalTimeMillis);
+ verifyNoMoreInteractions(mPersistAtomsStorage);
+ }
+
+ private void mockWwanPsRat(@NetworkType int rat) {
+ doReturn(new NetworkRegistrationInfo.Builder().setAccessNetworkTechnology(rat).build())
+ .when(mServiceState)
+ .getNetworkRegistrationInfo(
+ NetworkRegistrationInfo.DOMAIN_PS,
+ AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
+ }
+
+ private void mockDualSim(int carrierId) {
+ doReturn(1).when(mSecondPhone).getPhoneId();
+ doReturn(1).when(mUiccController).getSlotIdFromPhoneId(1);
+ doReturn(carrierId).when(mSecondPhone).getCarrierId();
+
+ doReturn(true).when(mPhysicalSlot1).isActive();
+ doReturn(CardState.CARDSTATE_PRESENT).when(mPhysicalSlot1).getCardState();
+ doReturn(false).when(mPhysicalSlot1).isEuicc();
+ doReturn(new UiccSlot[] {mPhysicalSlot0, mPhysicalSlot1})
+ .when(mUiccController)
+ .getUiccSlots();
+ doReturn(mPhysicalSlot1).when(mUiccController).getUiccSlot(1);
+ doReturn(mPhysicalSlot1).when(mUiccController).getUiccSlotForPhone(1);
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/SimSlotStateTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/SimSlotStateTest.java
index 0f1196f..e05faec 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/SimSlotStateTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/SimSlotStateTest.java
@@ -17,6 +17,8 @@
package com.android.internal.telephony.metrics;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
@@ -73,10 +75,12 @@
doReturn(new UiccSlot[] {}).when(mUiccController).getUiccSlots();
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(0, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -85,10 +89,12 @@
setupSingleSim(null);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(0, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -97,10 +103,12 @@
setupSingleSim(mInactiveSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(0, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -109,10 +117,12 @@
setupSingleSim(mEmptySlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -122,10 +132,12 @@
setupSingleSim(mPhysicalSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(1, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -135,10 +147,12 @@
setupSingleSim(mPhysicalSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -148,11 +162,13 @@
setupSingleSim(mPhysicalSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
// the metrics should not count restricted cards since they cannot be used
assertEquals(1, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -162,10 +178,12 @@
setupSingleSim(mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -175,10 +193,12 @@
setupSingleSim(mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -188,10 +208,12 @@
setupSingleSim(mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(1, state.numActiveSims);
assertEquals(1, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -200,10 +222,12 @@
setupDualSim(mEmptySlot, mInactiveSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -212,10 +236,12 @@
setupDualSim(mPhysicalSlot, mInactiveSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(1, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -225,10 +251,12 @@
setupDualSim(mInactiveSlot, mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -238,10 +266,12 @@
setupDualSim(mInactiveSlot, mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(1, state.numActiveSlots);
assertEquals(1, state.numActiveSims);
assertEquals(1, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -251,10 +281,12 @@
setupDualSim(mEmptySlot, mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(2, state.numActiveSlots);
assertEquals(0, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -264,10 +296,12 @@
setupDualSim(mPhysicalSlot, mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(2, state.numActiveSlots);
assertEquals(1, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -277,10 +311,12 @@
setupDualSim(mEmptySlot, mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(2, state.numActiveSlots);
assertEquals(1, state.numActiveSims);
assertEquals(1, state.numActiveEsims);
+ assertFalse(isMultiSim);
}
@Test
@@ -290,10 +326,12 @@
setupDualSim(mPhysicalSlot, mEsimSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(2, state.numActiveSlots);
assertEquals(2, state.numActiveSims);
assertEquals(1, state.numActiveEsims);
+ assertTrue(isMultiSim);
}
@Test
@@ -302,10 +340,45 @@
setupDualSim(mPhysicalSlot, mPhysicalSlot);
SimSlotState state = SimSlotState.getCurrentState();
+ boolean isMultiSim = SimSlotState.isMultiSim();
assertEquals(2, state.numActiveSlots);
assertEquals(2, state.numActiveSims);
assertEquals(0, state.numActiveEsims);
+ assertTrue(isMultiSim);
+ }
+
+ @Test
+ @SmallTest
+ public void isEsim_singlePhysicalSim() {
+ doReturn(mPhysicalSlot).when(mUiccController).getUiccSlotForPhone(eq(0));
+
+ boolean isEsim = SimSlotState.isEsim(0);
+
+ assertFalse(isEsim);
+ }
+
+ @Test
+ @SmallTest
+ public void isEsim_singleEsim() {
+ doReturn(mEsimSlot).when(mUiccController).getUiccSlotForPhone(eq(0));
+
+ boolean isEsim = SimSlotState.isEsim(0);
+
+ assertTrue(isEsim);
+ }
+
+ @Test
+ @SmallTest
+ public void isEsim_dualSim() {
+ doReturn(mPhysicalSlot).when(mUiccController).getUiccSlotForPhone(eq(0));
+ doReturn(mEsimSlot).when(mUiccController).getUiccSlotForPhone(eq(1));
+
+ boolean isEsim0 = SimSlotState.isEsim(0);
+ boolean isEsim1 = SimSlotState.isEsim(1);
+
+ assertFalse(isEsim0);
+ assertTrue(isEsim1);
}
private void setupSingleSim(UiccSlot slot0) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
index e11cddd..f64bc6e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/VoiceCallSessionStatsTest.java
@@ -20,11 +20,12 @@
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;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
+import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_FAST;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
-import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST;
import static com.android.internal.telephony.TelephonyStatsLog.VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW;
import static com.google.common.truth.Truth.assertThat;
@@ -57,7 +58,7 @@
import com.android.internal.telephony.TelephonyTest;
import com.android.internal.telephony.imsphone.ImsPhoneCall;
import com.android.internal.telephony.imsphone.ImsPhoneConnection;
-import com.android.internal.telephony.nano.PersistAtomsProto.RawVoiceCallRatUsage;
+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;
import com.android.internal.telephony.protobuf.nano.MessageNano;
@@ -133,6 +134,7 @@
doReturn(CARRIER_ID_SLOT_0).when(mPhone).getCarrierId();
// mPhone's mSST/mServiceState has been set up by TelephonyTest
doReturn(CARRIER_ID_SLOT_1).when(mSecondPhone).getCarrierId();
+ doReturn(mSignalStrength).when(mSecondPhone).getSignalStrength();
doReturn(mSecondServiceStateTracker).when(mSecondPhone).getServiceStateTracker();
doReturn(mSecondServiceState).when(mSecondServiceStateTracker).getServiceState();
@@ -160,6 +162,7 @@
doReturn(new UiccSlot[] {mPhysicalSlot}).when(mUiccController).getUiccSlots();
doReturn(mPhysicalSlot).when(mUiccController).getUiccSlot(eq(0));
+ doReturn(mPhysicalSlot).when(mUiccController).getUiccSlotForPhone(eq(0));
doReturn(0).when(mUiccController).getSlotIdFromPhoneId(0);
doReturn(1).when(mUiccController).getSlotIdFromPhoneId(1);
@@ -199,13 +202,18 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_REMOTE_CALL_DECLINE);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 200;
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_EVS_SWB;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.DIALING).when(mImsCall0).getState();
@@ -247,10 +255,11 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_SIP_FORBIDDEN);
expectedCall.setupFailed = true;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 2200L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.DIALING).when(mImsCall0).getState();
@@ -285,14 +294,17 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 200;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_EVS_SWB;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_SUPER_WIDEBAND;
expectedCall.disconnectExtraMessage = "normal call clearing";
- RawVoiceCallRatUsage expectedRatUsage =
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 100000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.DIALING).when(mImsCall0).getState();
@@ -340,11 +352,14 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 8000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -382,13 +397,16 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -423,6 +441,7 @@
doReturn(mInactiveCard).when(mEsimSlot).getUiccCard();
doReturn(new UiccSlot[] {mPhysicalSlot, mEsimSlot}).when(mUiccController).getUiccSlots();
doReturn(mEsimSlot).when(mUiccController).getUiccSlot(eq(1));
+ doReturn(mEsimSlot).when(mUiccController).getUiccSlotForPhone(eq(1));
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
@@ -435,7 +454,10 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.isMultiSim = false; // DSDS with one active SIM profile should not count
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -463,6 +485,7 @@
doReturn(mActiveCard).when(mEsimSlot).getUiccCard();
doReturn(new UiccSlot[] {mPhysicalSlot, mEsimSlot}).when(mUiccController).getUiccSlots();
doReturn(mEsimSlot).when(mUiccController).getUiccSlot(eq(1));
+ doReturn(mEsimSlot).when(mUiccController).getUiccSlotForPhone(eq(1));
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
doReturn(true).when(mImsConnection0).isIncoming();
doReturn(2000L).when(mImsConnection0).getCreateTime();
@@ -475,7 +498,10 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.isMultiSim = true;
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -503,6 +529,7 @@
doReturn(mActiveCard).when(mEsimSlot).getUiccCard();
doReturn(new UiccSlot[] {mPhysicalSlot, mEsimSlot}).when(mUiccController).getUiccSlots();
doReturn(mEsimSlot).when(mUiccController).getUiccSlot(eq(1));
+ doReturn(mEsimSlot).when(mUiccController).getUiccSlotForPhone(eq(1));
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mSecondServiceState).getVoiceNetworkType();
doReturn(true).when(mImsConnection1).isIncoming();
doReturn(2000L).when(mImsConnection1).getCreateTime();
@@ -515,7 +542,10 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
mVoiceCallSessionStats1.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall1).getState();
@@ -552,7 +582,10 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.isEmergency = true;
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -590,7 +623,10 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.isRoaming = true;
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -627,10 +663,13 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.codecBitmask =
1L << AudioCodec.AUDIO_CODEC_AMR | 1L << AudioCodec.AUDIO_CODEC_EVS_SWB;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -675,21 +714,25 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.ratSwitchCount = 2L;
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall.bandAtEnd = 0;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 4000L, 1L);
- RawVoiceCallRatUsage expectedRatUsageHspa =
+ VoiceCallRatUsage expectedRatUsageHspa =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_HSPA, 4000L, 6000L, 1L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 6000L, 8000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -721,7 +764,7 @@
verifyNoMoreInteractions(mPersistAtomsStorage);
assertProtoEquals(expectedCall, callCaptor.getValue());
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {
+ new VoiceCallRatUsage[] {
expectedRatUsageLte, expectedRatUsageHspa, expectedRatUsageUmts
},
ratUsage.get());
@@ -743,7 +786,10 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.rttEnabled = true;
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -780,9 +826,12 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall.rttEnabled = true;
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -828,13 +877,17 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE);
expectedCall0.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall0.setupDurationMillis = 80;
expectedCall0.setupFailed = false;
expectedCall0.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall0.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall0.concurrentCallCountAtStart = 0;
expectedCall0.concurrentCallCountAtEnd = 1;
expectedCall0.ratSwitchCount = 1L;
expectedCall0.ratAtEnd = TelephonyManager.NETWORK_TYPE_HSPA;
+ expectedCall0.bandAtEnd = 0;
// call 1 starts later, MT
doReturn(true).when(mImsConnection1).isIncoming();
doReturn(60000L).when(mImsConnection1).getCreateTime();
@@ -848,26 +901,30 @@
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall1.setupDuration =
VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall1.setupDurationMillis = 20;
expectedCall1.setupFailed = false;
expectedCall1.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall1.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall1.concurrentCallCountAtStart = 1;
expectedCall1.concurrentCallCountAtEnd = 0;
expectedCall1.ratSwitchCount = 2L;
expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall1.bandAtEnd = 0;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
- RawVoiceCallRatUsage expectedRatUsageHspa =
+ VoiceCallRatUsage expectedRatUsageHspa =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_HSPA, 80000L, 100000L, 2L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0,
TelephonyManager.NETWORK_TYPE_UMTS,
100000L,
120000L,
1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
// call 0 dial
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -926,7 +983,7 @@
new VoiceCallSession[] {expectedCall0, expectedCall1},
callCaptor.getAllValues().stream().toArray(VoiceCallSession[]::new));
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {
+ new VoiceCallRatUsage[] {
expectedRatUsageLte, expectedRatUsageHspa, expectedRatUsageUmts
},
ratUsage.get());
@@ -948,13 +1005,17 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall0.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall0.setupDurationMillis = 80;
expectedCall0.setupFailed = false;
expectedCall0.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall0.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall0.concurrentCallCountAtStart = 0;
expectedCall0.concurrentCallCountAtEnd = 0;
expectedCall0.ratSwitchCount = 2L;
expectedCall0.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall0.bandAtEnd = 0;
// call 1 starts later, MT
doReturn(true).when(mImsConnection1).isIncoming();
doReturn(60000L).when(mImsConnection1).getCreateTime();
@@ -968,26 +1029,30 @@
ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE);
expectedCall1.setupDuration =
VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall1.setupDurationMillis = 20;
expectedCall1.setupFailed = false;
expectedCall1.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall1.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall1.concurrentCallCountAtStart = 1;
expectedCall1.concurrentCallCountAtEnd = 1;
expectedCall1.ratSwitchCount = 1L;
expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_HSPA;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall1.bandAtEnd = 0;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
- RawVoiceCallRatUsage expectedRatUsageHspa =
+ VoiceCallRatUsage expectedRatUsageHspa =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_HSPA, 80000L, 100000L, 2L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0,
TelephonyManager.NETWORK_TYPE_UMTS,
100000L,
120000L,
1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
// call 0 dial
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -1046,7 +1111,7 @@
new VoiceCallSession[] {expectedCall0, expectedCall1},
callCaptor.getAllValues().stream().toArray(VoiceCallSession[]::new));
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {
+ new VoiceCallRatUsage[] {
expectedRatUsageLte, expectedRatUsageHspa, expectedRatUsageUmts
},
ratUsage.get());
@@ -1068,9 +1133,12 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall0.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall0.setupDurationMillis = 80;
expectedCall0.setupFailed = false;
expectedCall0.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall0.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall0.concurrentCallCountAtStart = 0;
expectedCall0.concurrentCallCountAtEnd = 1;
expectedCall0.ratSwitchCount = 0L;
@@ -1088,19 +1156,23 @@
ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE);
expectedCall1.setupDuration =
VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall1.setupDurationMillis = 20;
expectedCall1.setupFailed = false;
expectedCall1.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall1.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall1.concurrentCallCountAtStart = 1;
expectedCall1.concurrentCallCountAtEnd = 0;
expectedCall1.ratSwitchCount = 1L;
expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_HSPA;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall1.bandAtEnd = 0;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
- RawVoiceCallRatUsage expectedRatUsageHspa =
+ VoiceCallRatUsage expectedRatUsageHspa =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_HSPA, 80000L, 90000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
// call 0 dial
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -1155,7 +1227,7 @@
new VoiceCallSession[] {expectedCall0, expectedCall1},
callCaptor.getAllValues().stream().toArray(VoiceCallSession[]::new));
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageHspa},
+ new VoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageHspa},
ratUsage.get());
}
@@ -1172,19 +1244,24 @@
TelephonyManager.NETWORK_TYPE_LTE,
DisconnectCause.NORMAL);
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall.bandAtEnd = 0;
expectedCall.setupDuration =
VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW;
+ expectedCall.setupDurationMillis = 5000;
expectedCall.disconnectExtraCode = PreciseDisconnectCause.CALL_REJECTED;
expectedCall.ratSwitchCount = 1L;
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 3000L, 1L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 3000L, 15000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
@@ -1215,7 +1292,7 @@
verifyNoMoreInteractions(mPersistAtomsStorage);
assertProtoEquals(expectedCall, callCaptor.getValue());
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
+ new VoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
ratUsage.get());
}
@@ -1232,16 +1309,20 @@
TelephonyManager.NETWORK_TYPE_LTE,
DisconnectCause.LOST_SIGNAL);
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall.bandAtEnd = 0;
expectedCall.ratSwitchCount = 1L;
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 3000L, 1L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 3000L, 15000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
@@ -1265,7 +1346,7 @@
verifyNoMoreInteractions(mPersistAtomsStorage);
assertProtoEquals(expectedCall, callCaptor.getValue());
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
+ new VoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
ratUsage.get());
}
@@ -1281,20 +1362,25 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MO,
TelephonyManager.NETWORK_TYPE_LTE,
DisconnectCause.NORMAL);
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UMTS;
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall.bandAtEnd = 0;
expectedCall.setupDuration =
VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_VERY_SLOW;
+ expectedCall.setupDurationMillis = 5000;
expectedCall.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
expectedCall.ratSwitchCount = 1L;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 3000L, 1L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 3000L, 100000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(TelephonyManager.NETWORK_TYPE_LTE).when(mServiceState).getVoiceNetworkType();
@@ -1327,7 +1413,7 @@
verifyNoMoreInteractions(mPersistAtomsStorage);
assertProtoEquals(expectedCall, callCaptor.getValue());
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
+ new VoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
ratUsage.get());
}
@@ -1346,13 +1432,18 @@
DisconnectCause.NORMAL);
expectedCall.setupDuration =
VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
+ expectedCall.setupDurationMillis = 0;
expectedCall.disconnectExtraCode = PreciseDisconnectCause.CALL_REJECTED;
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ expectedCall.bandAtEnd = 0;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 2500L, 15000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
@@ -1393,14 +1484,19 @@
VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT,
TelephonyManager.NETWORK_TYPE_UMTS,
DisconnectCause.NORMAL);
- expectedCall.setupDuration = VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_FAST;
+ expectedCall.setupDuration =
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ expectedCall.setupDurationMillis = 500;
expectedCall.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
expectedCall.setupFailed = false;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ expectedCall.bandAtEnd = 0;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 2500L, 100000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
mVoiceCallSessionStats0.onServiceStateChanged(mServiceState);
@@ -1449,19 +1545,23 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.srvccFailureCount = 2L;
expectedCall.ratSwitchCount = 1L;
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall.bandAtEnd = 0;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 10000L, 1L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 10000L, 12000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -1502,7 +1602,7 @@
verifyNoMoreInteractions(mPersistAtomsStorage);
assertProtoEquals(expectedCall, callCaptor.getValue());
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
+ new VoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
ratUsage.get());
}
@@ -1523,14 +1623,17 @@
TelephonyManager.NETWORK_TYPE_LTE,
ImsReasonInfo.CODE_USER_TERMINATED);
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.srvccCancellationCount = 2L;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 12000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -1591,7 +1694,8 @@
DisconnectCause.NORMAL);
expectedCall.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
expectedCall.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall.setupDurationMillis = 80;
expectedCall.setupFailed = false;
expectedCall.srvccCancellationCount = 1L;
expectedCall.srvccFailureCount = 1L;
@@ -1599,14 +1703,17 @@
expectedCall.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
expectedCall.ratSwitchCount = 1L;
expectedCall.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall.bandAtEnd = 0;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 7000L, 1L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 7000L, 12000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
// IMS call created
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -1656,7 +1763,7 @@
verifyNoMoreInteractions(mPersistAtomsStorage);
assertProtoEquals(expectedCall, callCaptor.getValue());
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
+ new VoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
ratUsage.get());
}
@@ -1682,13 +1789,17 @@
DisconnectCause.NORMAL);
expectedCall0.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
expectedCall0.setupDuration =
- VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_ULTRA_FAST;
+ VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall0.setupDurationMillis = 80;
expectedCall0.setupFailed = false;
expectedCall0.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall0.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall0.concurrentCallCountAtStart = 0;
expectedCall0.concurrentCallCountAtEnd = 1;
expectedCall0.ratSwitchCount = 1L;
expectedCall0.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall0.bandAtEnd = 0;
expectedCall0.srvccCompleted = true;
expectedCall0.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
// call 1 starts later, MT
@@ -1707,21 +1818,25 @@
expectedCall1.disconnectExtraCode = PreciseDisconnectCause.NORMAL;
expectedCall1.setupDuration =
VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_EXTREMELY_FAST;
+ expectedCall1.setupDurationMillis = 20;
expectedCall1.setupFailed = false;
expectedCall1.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
+ expectedCall1.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
expectedCall1.concurrentCallCountAtStart = 1;
expectedCall1.concurrentCallCountAtEnd = 0;
expectedCall1.ratSwitchCount = 1L;
expectedCall1.ratAtEnd = TelephonyManager.NETWORK_TYPE_UMTS;
+ expectedCall1.bandAtEnd = 0;
expectedCall1.srvccCompleted = true;
expectedCall1.bearerAtEnd = VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_CS;
- RawVoiceCallRatUsage expectedRatUsageLte =
+ VoiceCallRatUsage expectedRatUsageLte =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_LTE, 2000L, 80000L, 2L);
- RawVoiceCallRatUsage expectedRatUsageUmts =
+ VoiceCallRatUsage expectedRatUsageUmts =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_UMTS, 80000L, 120000L, 2L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
// call 0 dial
mVoiceCallSessionStats0.setTimeMillis(2000L);
@@ -1784,7 +1899,7 @@
new VoiceCallSession[] {expectedCall0, expectedCall1},
callCaptor.getAllValues().stream().toArray(VoiceCallSession[]::new));
assertSortedProtoArrayEquals(
- new RawVoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
+ new VoiceCallRatUsage[] {expectedRatUsageLte, expectedRatUsageUmts},
ratUsage.get());
}
@@ -1806,11 +1921,15 @@
TelephonyManager.NETWORK_TYPE_IWLAN,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ expectedCall.bandAtEnd = 0;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_IWLAN, 2000L, 8000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -1851,11 +1970,15 @@
TelephonyManager.NETWORK_TYPE_IWLAN,
ImsReasonInfo.CODE_LOCAL_CALL_DECLINE);
expectedCall.setupFailed = true;
+ expectedCall.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ expectedCall.bandAtEnd = 0;
expectedCall.codecBitmask = 1L << AudioCodec.AUDIO_CODEC_AMR;
- RawVoiceCallRatUsage expectedRatUsage =
+ expectedCall.mainCodecQuality =
+ VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_NARROWBAND;
+ VoiceCallRatUsage expectedRatUsage =
makeRatUsageProto(
CARRIER_ID_SLOT_0, TelephonyManager.NETWORK_TYPE_IWLAN, 2000L, 8000L, 1L);
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = setupRatUsageCapture();
mVoiceCallSessionStats0.setTimeMillis(2000L);
doReturn(Call.State.INCOMING).when(mImsCall0).getState();
@@ -1878,8 +2001,8 @@
assertProtoEquals(expectedRatUsage, ratUsage.get()[0]);
}
- private AtomicReference<RawVoiceCallRatUsage[]> setupRatUsageCapture() {
- final AtomicReference<RawVoiceCallRatUsage[]> ratUsage = new AtomicReference<>(null);
+ private AtomicReference<VoiceCallRatUsage[]> setupRatUsageCapture() {
+ final AtomicReference<VoiceCallRatUsage[]> ratUsage = new AtomicReference<>(null);
doAnswer(invocation -> {
VoiceCallRatTracker tracker = (VoiceCallRatTracker) invocation.getArguments()[0];
ratUsage.set(tracker.toProto());
@@ -1895,14 +2018,18 @@
call.bearerAtEnd = bearer;
call.direction = direction;
call.setupDuration = VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
+ call.setupDurationMillis = 0;
call.setupFailed = true;
call.disconnectReasonCode = reason;
call.disconnectExtraCode = 0;
call.disconnectExtraMessage = "";
call.ratAtStart = rat;
+ call.ratAtConnected = rat;
call.ratAtEnd = rat;
+ call.bandAtEnd = 1;
call.ratSwitchCount = 0L;
call.codecBitmask = 0L;
+ call.mainCodecQuality = VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
call.simSlotIndex = 0;
call.isMultiSim = false;
call.isEsim = false;
@@ -1914,6 +2041,7 @@
call.isEmergency = false;
call.isRoaming = false;
call.setupBeginMillis = 0L;
+ call.signalStrengthAtEnd = 2;
return call;
}
@@ -1924,14 +2052,18 @@
call.bearerAtEnd = bearer;
call.direction = direction;
call.setupDuration = VOICE_CALL_SESSION__SETUP_DURATION__CALL_SETUP_DURATION_UNKNOWN;
+ call.setupDurationMillis = 0;
call.setupFailed = true;
call.disconnectReasonCode = reason;
call.disconnectExtraCode = 0;
call.disconnectExtraMessage = "";
call.ratAtStart = rat;
+ call.ratAtConnected = rat;
call.ratAtEnd = rat;
+ call.bandAtEnd = 1;
call.ratSwitchCount = 0L;
call.codecBitmask = 0L;
+ call.mainCodecQuality = VOICE_CALL_SESSION__MAIN_CODEC_QUALITY__CODEC_QUALITY_UNKNOWN;
call.simSlotIndex = 1;
call.isMultiSim = true;
call.isEsim = true;
@@ -1943,12 +2075,13 @@
call.isEmergency = false;
call.isRoaming = false;
call.setupBeginMillis = 0L;
+ call.signalStrengthAtEnd = 2;
return call;
}
- private static RawVoiceCallRatUsage makeRatUsageProto(
+ private static VoiceCallRatUsage makeRatUsageProto(
int carrierId, int rat, long beginMillis, long endMillis, long callCount) {
- RawVoiceCallRatUsage usage = new RawVoiceCallRatUsage();
+ VoiceCallRatUsage usage = new VoiceCallRatUsage();
usage.carrierId = carrierId;
usage.rat = rat;
usage.totalDurationMillis = endMillis - beginMillis;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
index b383bfa..39ec59d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
@@ -26,6 +26,7 @@
import android.content.pm.Signature;
import android.os.AsyncResult;
import android.os.Message;
+import android.telephony.UiccAccessRule;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -41,11 +42,17 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.util.List;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class UiccCarrierPrivilegeRulesTest extends TelephonyTest {
private UiccCarrierPrivilegeRules mUiccCarrierPrivilegeRules;
+ private static final String ARAM = "A00000015141434C00";
+ private static final String ARAD = "A00000015144414300";
+ private static final String PKCS15_AID = "A000000063504B43532D3135";
+
@Mock
private UiccProfile mUiccProfile;
@@ -289,10 +296,6 @@
assertTrue(!mUiccCarrierPrivilegeRules.shouldRetry(ar, 0));
}
- private static final String ARAM = "A00000015141434C00";
- private static final String ARAD = "A00000015144414300";
- private static final String PKCS15_AID = "A000000063504B43532D3135";
-
@Test
@SmallTest
public void testAID_OnlyARAM() {
@@ -476,7 +479,7 @@
@Test
@SmallTest
- public void testAID_NeitherARAMorARAD() {
+ public void testAID_ARFFailed() {
final String hexString =
"FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+ "C652E616E64726F69642E617070732E6D79617070E30ADB080000000000000001";
@@ -515,8 +518,285 @@
assertTrue(!mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
}
+ @Test
+ @SmallTest
+ public void testAID_ARFSucceed() {
+ /**
+ * PKCS#15 application (AID: A0 00 00 00 63 50 4B 43 53 2D 31 35)
+ * -ODF (5031)
+ * A7 06 30 04 04 02 52 07
+ * -DODF (5207)
+ * A1 29 30 00 30 0F 0C 0D 47 50 20 53 45 20 41 63 63 20 43 74 6C A1 14 30 12
+ * 06 0A 2A 86 48 86 FC 6B 81 48 01 01 30 04 04 02 42 00
+ * -EF ACMain (4200)
+ * 30 10 04 08 01 02 03 04 05 06 07 08 30 04 04 02 43 00
+ * -EF ACRules (4300)
+ * 30 10 A0 08 04 06 A0 00 00 01 51 01 30 04 04 02 43 10
+ * -EF ACConditions1 (4310)
+ * 30 22
+ * 04 20
+ * B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51465350
+ * 30 22
+ * 04 20
+ * 4C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EAFE8226079EF6F676FD1859
+ */
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ String aid = (String) invocation.getArguments()[0];
+ Message message = (Message) invocation.getArguments()[2];
+ AsyncResult ar = new AsyncResult(null, null, null);
+ if (aid.equals(ARAM)) {
+ message.arg2 = 1;
+ } else if (aid.equals(ARAD)) {
+ message.arg2 = 0;
+ } else {
+ // PKCS15
+ ar = new AsyncResult(null, new int[]{2}, null);
+ }
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+ // Select files
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ AsyncResult ar = new AsyncResult(null, new int[]{2}, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xA4), eq(0x00),
+ eq(0x04), eq(0x02), anyString(), any(Message.class));
+
+ // Read binary - ODF
+ String odf = "A706300404025207";
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(odf));
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("5031"), any(Message.class));
+
+ // Read binary - DODF
+ String dodf =
+ "A1293000300F0C0D4750205345204163632043746CA11"
+ + "43012060A2A864886FC6B81480101300404024200";
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(dodf));
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("5207"), any(Message.class));
+
+ // Read binary - ACMF
+ String acmf = "301004080102030405060708300404024300";
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(acmf));
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("4200"), any(Message.class));
+
+ // Read binary - ACRF
+ String acrf = "3010A0080406FFFFFFFFFFFF300404024310";
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(acrf));
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("4300"), any(Message.class));
+
+ // Read binary - ACCF
+ String accf =
+ "30220420B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB514653503022042"
+ + "04C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EAFE8226079EF6F676FD1859";
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(accf));
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("4310"), any(Message.class));
+
+
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[1];
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+ mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(mUiccProfile, null);
+ processAllMessages();
+
+ assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+ assertEquals(2, mUiccCarrierPrivilegeRules.getAccessRules().size());
+ List<UiccAccessRule> accessRules = mUiccCarrierPrivilegeRules.getAccessRules();
+ UiccAccessRule accessRule1 = new UiccAccessRule(
+ IccUtils.hexStringToBytes(
+ "B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51465350"),
+ "",
+ 0x00);
+ assertTrue(accessRules.contains(accessRule1));
+ UiccAccessRule accessRule2 = new UiccAccessRule(
+ IccUtils.hexStringToBytes(
+ "4C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EAFE8226079EF6F676FD1859"),
+ "",
+ 0x00);
+ assertTrue(accessRules.contains(accessRule2));
+ }
+
+ @Test
+ @SmallTest
+ public void testAID_ARFFallbackToACRF() {
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ String aid = (String) invocation.getArguments()[0];
+ Message message = (Message) invocation.getArguments()[2];
+ AsyncResult ar = new AsyncResult(null, null, null);
+ if (aid.equals(ARAM)) {
+ message.arg2 = 1;
+ } else if (aid.equals(ARAD)) {
+ message.arg2 = 0;
+ } else {
+ // PKCS15
+ ar = new AsyncResult(null, new int[]{2}, null);
+ }
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+ // Select files
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ AsyncResult ar = new AsyncResult(null, new int[]{2}, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xA4), eq(0x00),
+ eq(0x04), eq(0x02), anyString(), any(Message.class));
+
+ // Read binary ODF failed
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, new byte[]{});
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("5031"), any(Message.class));
+
+ // Read binary - ACRF
+ String acrf = "3010A0080406FFFFFFFFFFFF300404024310";
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(acrf));
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("4300"), any(Message.class));
+
+ // Read binary - ACCF
+ String accf =
+ "30220420B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB514653503022042"
+ + "04C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EAFE8226079EF6F676FD1859";
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[7];
+ IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(accf));
+ AsyncResult ar = new AsyncResult(null, iir, null);
+ message.obj = ar;
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccTransmitApduLogicalChannel(anyInt(), eq(0x00), eq(0xB0), eq(0x00),
+ eq(0x00), eq(0x00), eq("4310"), any(Message.class));
+
+
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Message message = (Message) invocation.getArguments()[1];
+ message.sendToTarget();
+ return null;
+ }
+ }).when(mUiccProfile).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+ mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(mUiccProfile, null);
+ processAllMessages();
+
+ assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+ assertEquals(2, mUiccCarrierPrivilegeRules.getAccessRules().size());
+ List<UiccAccessRule> accessRules = mUiccCarrierPrivilegeRules.getAccessRules();
+ UiccAccessRule accessRule1 = new UiccAccessRule(
+ IccUtils.hexStringToBytes(
+ "B9CFCE1C47A6AC713442718F15EF55B00B3A6D1A6D48CB46249FA8EB51465350"),
+ "",
+ 0x00);
+ assertTrue(accessRules.contains(accessRule1));
+ UiccAccessRule accessRule2 = new UiccAccessRule(
+ IccUtils.hexStringToBytes(
+ "4C36AF4A5BDAD97C1F3D8B283416D244496C2AC5EAFE8226079EF6F676FD1859"),
+ "",
+ 0x00);
+ assertTrue(accessRules.contains(accessRule2));
+ }
+
private static final int P2 = 0x40;
private static final int P2_EXTENDED_DATA = 0x60;
+
@Test
@SmallTest
public void testAID_RetransmitLogicalChannel() {