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

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

Log service state to statsd.

Bug: 161573530
Test: statsd_testdrive, atest FrameworksTelephonyTests
Change-Id: Ic5355bb40a61914a4755cf580f20e55164992847
parent 3cd71362
Loading
Loading
Loading
Loading
+41 −3
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@ option java_outer_classname = "PersistAtomsProto";

// 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: 13
// Next id: 17
message PersistAtoms {
    /* Aggregated RAT usage during the call. */
    repeated RawVoiceCallRatUsage raw_voice_call_rat_usage = 1;
@@ -60,6 +60,18 @@ message PersistAtoms {

    /* 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;
}

// The canonical versions of the following enums live in:
@@ -99,11 +111,10 @@ message VoiceCallSession {
    optional int64 setup_begin_millis = 10001;
}

// Internal use only
message RawVoiceCallRatUsage {
    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;
}

@@ -166,3 +177,30 @@ message DataCallSession {
    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;
}
+19 −0
Original line number Diff line number Diff line
@@ -98,6 +98,7 @@ import com.android.internal.telephony.cdnr.CarrierDisplayNameResolver;
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;
@@ -478,6 +479,8 @@ public class ServiceStateTracker extends Handler {
    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.
@@ -644,6 +647,8 @@ public class ServiceStateTracker extends Handler {
        mPhone = phone;
        mCi = ci;

        mServiceStateStats = new ServiceStateStats(mPhone);

        mCdnr = new CarrierDisplayNameResolver(mPhone);

        mEriManager = TelephonyComponentFactory.getInstance().inject(EriManager.class.getName())
@@ -1675,6 +1680,7 @@ public class ServiceStateTracker extends Handler {
                        TelephonyMetrics.getInstance().writeServiceStateChanged(
                                mPhone.getPhoneId(), mSS);
                        mPhone.getVoiceCallSessionStats().onServiceStateChanged(mSS);
                        mServiceStateStats.onServiceStateChanged(mSS);
                    }
                }
                break;
@@ -2259,6 +2265,7 @@ public class ServiceStateTracker extends Handler {
                    TelephonyMetrics.getInstance().writeServiceStateChanged(
                            mPhone.getPhoneId(), mSS);
                    mPhone.getVoiceCallSessionStats().onServiceStateChanged(mSS);
                    mServiceStateStats.onServiceStateChanged(mSS);
                }

                if (mPhone.isPhoneTypeGsm()) {
@@ -3547,6 +3554,7 @@ public class ServiceStateTracker extends Handler {
        if (hasChanged || hasNrStateChanged) {
            TelephonyMetrics.getInstance().writeServiceStateChanged(mPhone.getPhoneId(), mSS);
            mPhone.getVoiceCallSessionStats().onServiceStateChanged(mSS);
            mServiceStateStats.onServiceStateChanged(mSS);
        }

        boolean shouldLogAttachedChange = false;
@@ -5857,6 +5865,17 @@ public class ServiceStateTracker extends Handler {
        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.
     *
+98 −30
Original line number Diff line number Diff line
@@ -16,12 +16,13 @@

package com.android.internal.telephony.metrics;

import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID_LIST_VERSION;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
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.INCOMING_SMS;
import static com.android.internal.telephony.TelephonyStatsLog.OUTGOING_SMS;
@@ -39,6 +40,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.TelephonyStatsLog;
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.IncomingSms;
import com.android.internal.telephony.nano.PersistAtomsProto.OutgoingSms;
@@ -98,6 +101,8 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
        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);
@@ -131,6 +136,10 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
    @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:
@@ -167,7 +176,8 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
            return StatsManager.PULL_SKIP;
        }

        data.add(TelephonyStatsLog.buildStatsEvent(
        data.add(
                TelephonyStatsLog.buildStatsEvent(
                        SIM_SLOT_STATE,
                        state.numActiveSlots,
                        state.numActiveSims,
@@ -176,39 +186,29 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
    }

    private static int pullSupportedRadioAccessFamily(List<StatsEvent> data) {
        long rafSupported = 0L;
        try {
        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();
        }
        } catch (IllegalStateException e) {
            // Phones have not been made yet
            return StatsManager.PULL_SKIP;
        }

        data.add(TelephonyStatsLog.buildStatsEvent(
                  SUPPORTED_RADIO_ACCESS_FAMILY,
                  rafSupported));
        data.add(TelephonyStatsLog.buildStatsEvent(SUPPORTED_RADIO_ACCESS_FAMILY, rafSupported));
        return StatsManager.PULL_SUCCESS;
    }

    private static int pullCarrierIdTableVersion(List<StatsEvent> data) {
        int version = UNKNOWN_CARRIER_ID_LIST_VERSION;
        // All phones should have the same version of the carrier ID table, so only query
        // the first one.
        try {
            Phone phone = PhoneFactory.getPhone(0);
            if (phone != null) {
                version = phone.getCarrierIdListVersion();
            }
        } catch (IllegalStateException e) {
            // Nothing to do
        }

        if (version == UNKNOWN_CARRIER_ID_LIST_VERSION) {
        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(TelephonyStatsLog.buildStatsEvent(CARRIER_ID_TABLE_VERSION, version));
            return StatsManager.PULL_SUCCESS;
        }
@@ -284,11 +284,69 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
        }
    }

    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;
        }
    }

    /** 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(CellularDataServiceSwitch serviceSwitch) {
        return TelephonyStatsLog.buildStatsEvent(
                CELLULAR_DATA_SERVICE_SWITCH,
                serviceSwitch.ratFrom,
                serviceSwitch.ratTo,
                serviceSwitch.simSlotIndex,
                serviceSwitch.isMultiSim,
                serviceSwitch.carrierId,
                serviceSwitch.switchCount);
    }

    private static StatsEvent buildStatsEvent(CellularServiceState state) {
        return TelephonyStatsLog.buildStatsEvent(
                CELLULAR_SERVICE_STATE,
                state.voiceRat,
                state.dataRat,
                state.voiceRoamingType,
                state.dataRoamingType,
                state.isEndc,
                state.simSlotIndex,
                state.isMultiSim,
                state.carrierId,
                (int) (round(state.totalTimeMillis, DURATION_BUCKET_MILLIS) / SECOND_IN_MILLIS));
    }

    private static StatsEvent buildStatsEvent(RawVoiceCallRatUsage usage) {
        return TelephonyStatsLog.buildStatsEvent(
                VOICE_CALL_RAT_USAGE,
@@ -389,6 +447,16 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
                dataCallSession.ongoing);
    }

    /** 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;
Loading