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

Commit 7eaef8b4 authored by Chi Zhang's avatar Chi Zhang Committed by Android (Google) Code Review
Browse files

Merge "Snapshot data call metrics on every pull." into sc-v2-dev

parents fdff63fc 3ddf5ae4
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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;
+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);
    }

    /**
@@ -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;
            }
@@ -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;
    }
@@ -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();
+21 −0
Original line number Diff line number Diff line
@@ -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.
@@ -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) {
@@ -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 {
@@ -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)
+25 −2
Original line number Diff line number Diff line
@@ -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);
    }

@@ -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.
+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,