Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit da73d3f1 authored by Chi Zhang's avatar Chi Zhang
Browse files

Snapshot data call metrics on every pull.

Test: atest FrameworksTelephonyTests, manual tests
Bug: 200309022
Change-Id: I12e658c8afa90820d4339c1dac9174c6e2c988bc
parent 633de1fa
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -3634,7 +3634,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;
+38 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ public class DataCallSessionStats {
    public synchronized void onSetupDataCall(@ApnType int apnTypeBitMask) {
        mDataCallSession = getDefaultProto(apnTypeBitMask);
        mStartTime = getTimeMillis();
        PhoneFactory.getMetricsCollector().registerOngoingDataCallStat(this);
    }

    /**
@@ -116,6 +117,7 @@ public class DataCallSessionStats {
                mDataCallSession.oosAtEnd = getIsOos();
                mDataCallSession.setupFailed = true;
                mDataCallSession.ongoing = false;
                PhoneFactory.getMetricsCollector().unregisterOngoingDataCallStat(this);
                mAtomsStorage.addDataCallSession(mDataCallSession);
                mDataCallSession = null;
            }
@@ -173,6 +175,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;
    }
@@ -198,10 +201,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();
+21 −0
Original line number Diff line number Diff line
@@ -60,6 +60,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.
@@ -101,6 +103,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) {
@@ -182,6 +185,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 {
@@ -299,6 +315,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)
+24 −2
Original line number Diff line number Diff line
@@ -233,8 +233,17 @@ 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, mMaxNumDataCallSessions);
        }

        saveAtomsToFile(SAVE_TO_FILE_DELAY_FOR_UPDATE_MILLIS);
    }

@@ -729,6 +738,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.
+75 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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();
@@ -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 {
@@ -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 {
@@ -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,