Loading src/java/com/android/internal/telephony/dataconnection/DataConnection.java +1 −1 Original line number Diff line number Diff line Loading @@ -3687,7 +3687,7 @@ public class DataConnection extends StateMachine { } } /** Sets the {@link DataCallSessionStats} mock for this phone ID during unit testing. */ /** Sets the {@link DataCallSessionStats} mock for this data connection during unit testing. */ @VisibleForTesting public void setDataCallSessionStats(DataCallSessionStats dataCallSessionStats) { mDataCallSessionStats = dataCallSessionStats; Loading src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java +38 −0 Original line number Diff line number Diff line Loading @@ -67,6 +67,7 @@ public class DataCallSessionStats { public synchronized void onSetupDataCall(@ApnType int apnTypeBitMask) { mDataCallSession = getDefaultProto(apnTypeBitMask); mStartTime = getTimeMillis(); PhoneFactory.getMetricsCollector().registerOngoingDataCallStat(this); } /** Loading Loading @@ -107,6 +108,7 @@ public class DataCallSessionStats { mDataCallSession.failureCause = failureCause; mDataCallSession.setupFailed = true; mDataCallSession.ongoing = false; PhoneFactory.getMetricsCollector().unregisterOngoingDataCallStat(this); mAtomsStorage.addDataCallSession(mDataCallSession); mDataCallSession = null; } Loading Loading @@ -161,6 +163,7 @@ public class DataCallSessionStats { mDataCallSession.durationMinutes = convertMillisToMinutes(getTimeMillis() - mStartTime); // store for the data call list event, after DataCall is disconnected and entered into // inactive mode PhoneFactory.getMetricsCollector().unregisterOngoingDataCallStat(this); mAtomsStorage.addDataCallSession(mDataCallSession); mDataCallSession = null; } Loading @@ -183,10 +186,45 @@ public class DataCallSessionStats { } } /** Add the on-going data call segment to the atom storage. */ public synchronized void conclude() { if (mDataCallSession != null) { DataCallSession call = copyOf(mDataCallSession); long nowMillis = getTimeMillis(); call.durationMinutes = convertMillisToMinutes(nowMillis - mStartTime); mStartTime = nowMillis; mDataCallSession.ratSwitchCount = 0L; mAtomsStorage.addDataCallSession(call); } } private static long convertMillisToMinutes(long millis) { return Math.round(millis / 60000.0); } private static DataCallSession copyOf(DataCallSession call) { DataCallSession copy = new DataCallSession(); copy.dimension = call.dimension; copy.isMultiSim = call.isMultiSim; copy.isEsim = call.isEsim; copy.apnTypeBitmask = call.apnTypeBitmask; copy.carrierId = call.carrierId; copy.isRoaming = call.isRoaming; copy.ratAtEnd = call.ratAtEnd; copy.oosAtEnd = call.oosAtEnd; copy.ratSwitchCount = call.ratSwitchCount; copy.isOpportunistic = call.isOpportunistic; copy.ipType = call.ipType; copy.setupFailed = call.setupFailed; copy.failureCause = call.failureCause; copy.suggestedRetryMillis = call.suggestedRetryMillis; copy.deactivateReason = call.deactivateReason; copy.durationMinutes = call.durationMinutes; copy.ongoing = call.ongoing; copy.bandAtEnd = call.bandAtEnd; return copy; } /** Creates a proto for a normal {@code DataCallSession} with default values. */ private DataCallSession getDefaultProto(@ApnType int apnTypeBitmask) { DataCallSession proto = new DataCallSession(); Loading src/java/com/android/internal/telephony/metrics/MetricsCollector.java +21 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,8 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Random; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * Implements statsd pullers for Telephony. Loading Loading @@ -102,6 +104,7 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { private PersistAtomsStorage mStorage; private final StatsManager mStatsManager; private final AirplaneModeStats mAirplaneModeStats; private final Set<DataCallSessionStats> mOngoingDataCallStats = ConcurrentHashMap.newKeySet(); private static final Random sRandom = new Random(); public MetricsCollector(Context context) { Loading Loading @@ -183,6 +186,19 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { return mStorage; } /** * Registers a {@link DataCallSessionStats} which will be pinged for on-going data calls when * data call atoms are pulled. */ public void registerOngoingDataCallStat(DataCallSessionStats call) { mOngoingDataCallStats.add(call); } /** Unregisters a {@link DataCallSessionStats} when it no longer handles an active data call. */ public void unregisterOngoingDataCallStat(DataCallSessionStats call) { mOngoingDataCallStats.remove(call); } private static int pullSimSlotState(List<StatsEvent> data) { SimSlotState state; try { Loading Loading @@ -289,6 +305,11 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { } private int pullDataCallSession(List<StatsEvent> data) { // Include ongoing data call segments for (DataCallSessionStats stats : mOngoingDataCallStats) { stats.conclude(); } DataCallSession[] dataCallSessions = mStorage.getDataCallSessions(MIN_COOLDOWN_MILLIS); if (dataCallSessions != null) { Arrays.stream(dataCallSessions) Loading src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java +25 −2 Original line number Diff line number Diff line Loading @@ -210,8 +210,18 @@ public class PersistAtomsStorage { /** Adds a data call session to the storage. */ public synchronized void addDataCallSession(DataCallSession dataCall) { int index = findIndex(dataCall); if (index >= 0) { DataCallSession existingCall = mAtoms.dataCallSession[index]; dataCall.ratSwitchCount += existingCall.ratSwitchCount; dataCall.durationMinutes += existingCall.durationMinutes; mAtoms.dataCallSession[index] = dataCall; } else { mAtoms.dataCallSession = insertAtRandomPlace(mAtoms.dataCallSession, dataCall, MAX_NUM_DATA_CALL_SESSIONS); insertAtRandomPlace( mAtoms.dataCallSession, dataCall, MAX_NUM_DATA_CALL_SESSIONS); } saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); } Loading Loading @@ -704,6 +714,19 @@ public class PersistAtomsStorage { return null; } /** * Returns the index of data call session that has the same random dimension as the given one, * or -1 if it does not exist. */ private int findIndex(DataCallSession key) { for (int i = 0; i < mAtoms.dataCallSession.length; i++) { if (mAtoms.dataCallSession[i].dimension == key.dimension) { return i; } } return -1; } /** * Inserts a new element in a random position in an array with a maximum size, replacing the * least recent item if possible. Loading tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java +75 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ 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.DataCallSession; import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats; import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination; import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms; Loading Loading @@ -130,6 +131,10 @@ public class PersistAtomsStorageTest extends TelephonyTest { private ImsRegistrationStats[] mImsRegistrationStats; private ImsRegistrationTermination[] mImsRegistrationTerminations; // Data call sessions private DataCallSession mDataCallSession0; private DataCallSession mDataCallSession1; private void makeTestData() { // MO call with SRVCC (LTE to UMTS) mCall1Proto = new VoiceCallSession(); Loading Loading @@ -432,6 +437,24 @@ public class PersistAtomsStorageTest extends TelephonyTest { new ImsRegistrationTermination[] { mImsRegistrationTerminationLte, mImsRegistrationTerminationWifi }; mDataCallSession0 = new DataCallSession(); mDataCallSession0.dimension = 111; mDataCallSession0.carrierId = CARRIER1_ID; mDataCallSession0.oosAtEnd = false; mDataCallSession0.ratSwitchCount = 3L; mDataCallSession0.setupFailed = false; mDataCallSession0.durationMinutes = 20; mDataCallSession0.ongoing = true; mDataCallSession1 = new DataCallSession(); mDataCallSession1.dimension = 222; mDataCallSession1.carrierId = CARRIER2_ID; mDataCallSession1.oosAtEnd = true; mDataCallSession1.ratSwitchCount = 1L; mDataCallSession1.setupFailed = false; mDataCallSession1.durationMinutes = 5; mDataCallSession1.ongoing = false; } private static class TestablePersistAtomsStorage extends PersistAtomsStorage { Loading Loading @@ -1273,6 +1296,53 @@ public class PersistAtomsStorageTest extends TelephonyTest { inOrder.verifyNoMoreInteractions(); } @Test @SmallTest public void addDataCallSession_newEntry() throws Exception { createEmptyTestFile(); mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); mPersistAtomsStorage.addDataCallSession(mDataCallSession0); mPersistAtomsStorage.addDataCallSession(mDataCallSession1); mPersistAtomsStorage.incTimeMillis(100L); // there should be 2 data calls verifyCurrentStateSavedToFileOnce(); DataCallSession[] dataCalls = mPersistAtomsStorage.getDataCallSessions(0L); assertProtoArrayEqualsIgnoringOrder( new DataCallSession[] {mDataCallSession0, mDataCallSession1}, dataCalls); } @Test @SmallTest public void addDataCallSession_existingEntry() throws Exception { createEmptyTestFile(); mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); DataCallSession newDataCallSession0 = copyOf(mDataCallSession0); newDataCallSession0.ongoing = false; newDataCallSession0.ratAtEnd = TelephonyManager.NETWORK_TYPE_LTE; newDataCallSession0.durationMinutes = 10; newDataCallSession0.ratSwitchCount = 5; DataCallSession totalDataCallSession0 = copyOf(newDataCallSession0); totalDataCallSession0.durationMinutes = mDataCallSession0.durationMinutes + newDataCallSession0.durationMinutes; totalDataCallSession0.ratSwitchCount = mDataCallSession0.ratSwitchCount + newDataCallSession0.ratSwitchCount; mPersistAtomsStorage.addDataCallSession(mDataCallSession0); mPersistAtomsStorage.addDataCallSession(newDataCallSession0); mPersistAtomsStorage.incTimeMillis(100L); // there should be 1 data call verifyCurrentStateSavedToFileOnce(); DataCallSession[] dataCalls = mPersistAtomsStorage.getDataCallSessions(0L); assertProtoArrayEqualsIgnoringOrder( new DataCallSession[] {totalDataCallSession0}, dataCalls); } /* Utilities */ private void createEmptyTestFile() throws Exception { Loading Loading @@ -1351,6 +1421,11 @@ public class PersistAtomsStorageTest extends TelephonyTest { return ImsRegistrationTermination.parseFrom(MessageNano.toByteArray(source)); } private static DataCallSession copyOf(DataCallSession source) throws Exception { return DataCallSession.parseFrom(MessageNano.toByteArray(source)); } private void assertAllPullTimestampEquals(long timestamp) { assertEquals( timestamp, Loading Loading
src/java/com/android/internal/telephony/dataconnection/DataConnection.java +1 −1 Original line number Diff line number Diff line Loading @@ -3687,7 +3687,7 @@ public class DataConnection extends StateMachine { } } /** Sets the {@link DataCallSessionStats} mock for this phone ID during unit testing. */ /** Sets the {@link DataCallSessionStats} mock for this data connection during unit testing. */ @VisibleForTesting public void setDataCallSessionStats(DataCallSessionStats dataCallSessionStats) { mDataCallSessionStats = dataCallSessionStats; Loading
src/java/com/android/internal/telephony/metrics/DataCallSessionStats.java +38 −0 Original line number Diff line number Diff line Loading @@ -67,6 +67,7 @@ public class DataCallSessionStats { public synchronized void onSetupDataCall(@ApnType int apnTypeBitMask) { mDataCallSession = getDefaultProto(apnTypeBitMask); mStartTime = getTimeMillis(); PhoneFactory.getMetricsCollector().registerOngoingDataCallStat(this); } /** Loading Loading @@ -107,6 +108,7 @@ public class DataCallSessionStats { mDataCallSession.failureCause = failureCause; mDataCallSession.setupFailed = true; mDataCallSession.ongoing = false; PhoneFactory.getMetricsCollector().unregisterOngoingDataCallStat(this); mAtomsStorage.addDataCallSession(mDataCallSession); mDataCallSession = null; } Loading Loading @@ -161,6 +163,7 @@ public class DataCallSessionStats { mDataCallSession.durationMinutes = convertMillisToMinutes(getTimeMillis() - mStartTime); // store for the data call list event, after DataCall is disconnected and entered into // inactive mode PhoneFactory.getMetricsCollector().unregisterOngoingDataCallStat(this); mAtomsStorage.addDataCallSession(mDataCallSession); mDataCallSession = null; } Loading @@ -183,10 +186,45 @@ public class DataCallSessionStats { } } /** Add the on-going data call segment to the atom storage. */ public synchronized void conclude() { if (mDataCallSession != null) { DataCallSession call = copyOf(mDataCallSession); long nowMillis = getTimeMillis(); call.durationMinutes = convertMillisToMinutes(nowMillis - mStartTime); mStartTime = nowMillis; mDataCallSession.ratSwitchCount = 0L; mAtomsStorage.addDataCallSession(call); } } private static long convertMillisToMinutes(long millis) { return Math.round(millis / 60000.0); } private static DataCallSession copyOf(DataCallSession call) { DataCallSession copy = new DataCallSession(); copy.dimension = call.dimension; copy.isMultiSim = call.isMultiSim; copy.isEsim = call.isEsim; copy.apnTypeBitmask = call.apnTypeBitmask; copy.carrierId = call.carrierId; copy.isRoaming = call.isRoaming; copy.ratAtEnd = call.ratAtEnd; copy.oosAtEnd = call.oosAtEnd; copy.ratSwitchCount = call.ratSwitchCount; copy.isOpportunistic = call.isOpportunistic; copy.ipType = call.ipType; copy.setupFailed = call.setupFailed; copy.failureCause = call.failureCause; copy.suggestedRetryMillis = call.suggestedRetryMillis; copy.deactivateReason = call.deactivateReason; copy.durationMinutes = call.durationMinutes; copy.ongoing = call.ongoing; copy.bandAtEnd = call.bandAtEnd; return copy; } /** Creates a proto for a normal {@code DataCallSession} with default values. */ private DataCallSession getDefaultProto(@ApnType int apnTypeBitmask) { DataCallSession proto = new DataCallSession(); Loading
src/java/com/android/internal/telephony/metrics/MetricsCollector.java +21 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,8 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Random; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * Implements statsd pullers for Telephony. Loading Loading @@ -102,6 +104,7 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { private PersistAtomsStorage mStorage; private final StatsManager mStatsManager; private final AirplaneModeStats mAirplaneModeStats; private final Set<DataCallSessionStats> mOngoingDataCallStats = ConcurrentHashMap.newKeySet(); private static final Random sRandom = new Random(); public MetricsCollector(Context context) { Loading Loading @@ -183,6 +186,19 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { return mStorage; } /** * Registers a {@link DataCallSessionStats} which will be pinged for on-going data calls when * data call atoms are pulled. */ public void registerOngoingDataCallStat(DataCallSessionStats call) { mOngoingDataCallStats.add(call); } /** Unregisters a {@link DataCallSessionStats} when it no longer handles an active data call. */ public void unregisterOngoingDataCallStat(DataCallSessionStats call) { mOngoingDataCallStats.remove(call); } private static int pullSimSlotState(List<StatsEvent> data) { SimSlotState state; try { Loading Loading @@ -289,6 +305,11 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback { } private int pullDataCallSession(List<StatsEvent> data) { // Include ongoing data call segments for (DataCallSessionStats stats : mOngoingDataCallStats) { stats.conclude(); } DataCallSession[] dataCallSessions = mStorage.getDataCallSessions(MIN_COOLDOWN_MILLIS); if (dataCallSessions != null) { Arrays.stream(dataCallSessions) Loading
src/java/com/android/internal/telephony/metrics/PersistAtomsStorage.java +25 −2 Original line number Diff line number Diff line Loading @@ -210,8 +210,18 @@ public class PersistAtomsStorage { /** Adds a data call session to the storage. */ public synchronized void addDataCallSession(DataCallSession dataCall) { int index = findIndex(dataCall); if (index >= 0) { DataCallSession existingCall = mAtoms.dataCallSession[index]; dataCall.ratSwitchCount += existingCall.ratSwitchCount; dataCall.durationMinutes += existingCall.durationMinutes; mAtoms.dataCallSession[index] = dataCall; } else { mAtoms.dataCallSession = insertAtRandomPlace(mAtoms.dataCallSession, dataCall, MAX_NUM_DATA_CALL_SESSIONS); insertAtRandomPlace( mAtoms.dataCallSession, dataCall, MAX_NUM_DATA_CALL_SESSIONS); } saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS); } Loading Loading @@ -704,6 +714,19 @@ public class PersistAtomsStorage { return null; } /** * Returns the index of data call session that has the same random dimension as the given one, * or -1 if it does not exist. */ private int findIndex(DataCallSession key) { for (int i = 0; i < mAtoms.dataCallSession.length; i++) { if (mAtoms.dataCallSession[i].dimension == key.dimension) { return i; } } return -1; } /** * Inserts a new element in a random position in an array with a maximum size, replacing the * least recent item if possible. Loading
tests/telephonytests/src/com/android/internal/telephony/metrics/PersistAtomsStorageTest.java +75 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ 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.DataCallSession; import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationStats; import com.android.internal.telephony.nano.PersistAtomsProto.ImsRegistrationTermination; import com.android.internal.telephony.nano.PersistAtomsProto.PersistAtoms; Loading Loading @@ -130,6 +131,10 @@ public class PersistAtomsStorageTest extends TelephonyTest { private ImsRegistrationStats[] mImsRegistrationStats; private ImsRegistrationTermination[] mImsRegistrationTerminations; // Data call sessions private DataCallSession mDataCallSession0; private DataCallSession mDataCallSession1; private void makeTestData() { // MO call with SRVCC (LTE to UMTS) mCall1Proto = new VoiceCallSession(); Loading Loading @@ -432,6 +437,24 @@ public class PersistAtomsStorageTest extends TelephonyTest { new ImsRegistrationTermination[] { mImsRegistrationTerminationLte, mImsRegistrationTerminationWifi }; mDataCallSession0 = new DataCallSession(); mDataCallSession0.dimension = 111; mDataCallSession0.carrierId = CARRIER1_ID; mDataCallSession0.oosAtEnd = false; mDataCallSession0.ratSwitchCount = 3L; mDataCallSession0.setupFailed = false; mDataCallSession0.durationMinutes = 20; mDataCallSession0.ongoing = true; mDataCallSession1 = new DataCallSession(); mDataCallSession1.dimension = 222; mDataCallSession1.carrierId = CARRIER2_ID; mDataCallSession1.oosAtEnd = true; mDataCallSession1.ratSwitchCount = 1L; mDataCallSession1.setupFailed = false; mDataCallSession1.durationMinutes = 5; mDataCallSession1.ongoing = false; } private static class TestablePersistAtomsStorage extends PersistAtomsStorage { Loading Loading @@ -1273,6 +1296,53 @@ public class PersistAtomsStorageTest extends TelephonyTest { inOrder.verifyNoMoreInteractions(); } @Test @SmallTest public void addDataCallSession_newEntry() throws Exception { createEmptyTestFile(); mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); mPersistAtomsStorage.addDataCallSession(mDataCallSession0); mPersistAtomsStorage.addDataCallSession(mDataCallSession1); mPersistAtomsStorage.incTimeMillis(100L); // there should be 2 data calls verifyCurrentStateSavedToFileOnce(); DataCallSession[] dataCalls = mPersistAtomsStorage.getDataCallSessions(0L); assertProtoArrayEqualsIgnoringOrder( new DataCallSession[] {mDataCallSession0, mDataCallSession1}, dataCalls); } @Test @SmallTest public void addDataCallSession_existingEntry() throws Exception { createEmptyTestFile(); mPersistAtomsStorage = new TestablePersistAtomsStorage(mContext); DataCallSession newDataCallSession0 = copyOf(mDataCallSession0); newDataCallSession0.ongoing = false; newDataCallSession0.ratAtEnd = TelephonyManager.NETWORK_TYPE_LTE; newDataCallSession0.durationMinutes = 10; newDataCallSession0.ratSwitchCount = 5; DataCallSession totalDataCallSession0 = copyOf(newDataCallSession0); totalDataCallSession0.durationMinutes = mDataCallSession0.durationMinutes + newDataCallSession0.durationMinutes; totalDataCallSession0.ratSwitchCount = mDataCallSession0.ratSwitchCount + newDataCallSession0.ratSwitchCount; mPersistAtomsStorage.addDataCallSession(mDataCallSession0); mPersistAtomsStorage.addDataCallSession(newDataCallSession0); mPersistAtomsStorage.incTimeMillis(100L); // there should be 1 data call verifyCurrentStateSavedToFileOnce(); DataCallSession[] dataCalls = mPersistAtomsStorage.getDataCallSessions(0L); assertProtoArrayEqualsIgnoringOrder( new DataCallSession[] {totalDataCallSession0}, dataCalls); } /* Utilities */ private void createEmptyTestFile() throws Exception { Loading Loading @@ -1351,6 +1421,11 @@ public class PersistAtomsStorageTest extends TelephonyTest { return ImsRegistrationTermination.parseFrom(MessageNano.toByteArray(source)); } private static DataCallSession copyOf(DataCallSession source) throws Exception { return DataCallSession.parseFrom(MessageNano.toByteArray(source)); } private void assertAllPullTimestampEquals(long timestamp) { assertEquals( timestamp, Loading