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

Commit ac29d975 authored by Aswin Sankar's avatar Aswin Sankar Committed by Automerger Merge Worker
Browse files

Merge "DataCallSessionStats captures Cross-SIM calling" into udc-qpr-dev am: 2b102014

parents e51ebe3c 2b102014
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -365,6 +365,7 @@ message DataCallSession {
    repeated int32 handover_failure_causes = 20;
    repeated int32 handover_failure_rat = 21;
    optional bool is_non_dds = 22;
    optional bool is_iwlan_cross_sim = 23;
}

message CellularServiceState {
+54 −0
Original line number Diff line number Diff line
@@ -18,7 +18,13 @@ package com.android.internal.telephony.metrics;

import static com.android.internal.telephony.TelephonyStatsLog.DATA_CALL_SESSION__IP_TYPE__APN_PROTOCOL_IPV4;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.DataFailureCause;
@@ -55,6 +61,8 @@ public class DataCallSessionStats {
    private long mStartTime;
    @Nullable private DataCallSession mDataCallSession;

    private Network mSystemDefaultNetwork;
    private boolean mIsSystemDefaultNetworkMobile;
    private final PersistAtomsStorage mAtomsStorage =
            PhoneFactory.getMetricsCollector().getAtomsStorage();

@@ -62,8 +70,47 @@ public class DataCallSessionStats {

    public static final int SIZE_LIMIT_HANDOVER_FAILURES = 15;

    final class DefaultNetworkCallback extends ConnectivityManager.NetworkCallback {
        @Override
        public void onAvailable(@NonNull Network network) {
            mSystemDefaultNetwork = network;
        }

        @Override
        public void onCapabilitiesChanged(@NonNull Network network,
                                          @NonNull NetworkCapabilities nc) {
            if (network == mSystemDefaultNetwork) {
                mIsSystemDefaultNetworkMobile = nc.hasTransport(
                        NetworkCapabilities.TRANSPORT_CELLULAR);
            }
        }

        @Override
        public void onLost(@NonNull Network network) {
            mIsSystemDefaultNetworkMobile = false;
            mSystemDefaultNetwork = null;
        }
    }

    public DataCallSessionStats(Phone phone) {
        mPhone = phone;
        registerSystemDefaultNetworkCallback(phone);
    }

    private void registerSystemDefaultNetworkCallback(@NonNull Phone phone) {
        ConnectivityManager connectivityManager = phone.getContext()
                .getSystemService(ConnectivityManager.class);
        if (connectivityManager != null) {
            HandlerThread handlerThread = new HandlerThread(
                    DataCallSessionStats.class.getSimpleName());
            handlerThread.start();
            Handler callbackHandler = new Handler(handlerThread.getLooper());
            DefaultNetworkCallback mDefaultNetworkCallback = new DefaultNetworkCallback();
            connectivityManager.registerSystemDefaultNetworkCallback(
                    mDefaultNetworkCallback, callbackHandler);
        } else {
            loge("registerSystemDefaultNetworkCallback: ConnectivityManager is null!");
        }
    }

    /** Creates a new ongoing atom when data call is set up. */
@@ -101,6 +148,9 @@ public class DataCallSessionStats {
                    (currentRat == TelephonyManager.NETWORK_TYPE_IWLAN)
                            ? 0
                            : ServiceStateStats.getBand(mPhone);
            // Limitation: Will not capture IKE mobility between Backup Calling <-> WiFi Calling.
            mDataCallSession.isIwlanCrossSim = currentRat == TelephonyManager.NETWORK_TYPE_IWLAN
                    && mIsSystemDefaultNetworkMobile;
        }

        // only set if apn hasn't been set during setup
@@ -199,6 +249,8 @@ public class DataCallSessionStats {
            if (mDataCallSession.ratAtEnd != currentRat) {
                mDataCallSession.ratSwitchCount++;
                mDataCallSession.ratAtEnd = currentRat;
                mDataCallSession.isIwlanCrossSim = currentRat == TelephonyManager.NETWORK_TYPE_IWLAN
                        && mIsSystemDefaultNetworkMobile;
            }
            // band may have changed even if RAT was the same
            mDataCallSession.bandAtEnd =
@@ -288,6 +340,7 @@ public class DataCallSessionStats {
        copy.handoverFailureRat = Arrays.copyOf(call.handoverFailureRat,
                call.handoverFailureRat.length);
        copy.isNonDds = call.isNonDds;
        copy.isIwlanCrossSim = call.isIwlanCrossSim;
        return copy;
    }

@@ -313,6 +366,7 @@ public class DataCallSessionStats {
        proto.handoverFailureCauses = new int[0];
        proto.handoverFailureRat = new int[0];
        proto.isNonDds = false;
        proto.isIwlanCrossSim = false;
        return proto;
    }

+2 −1
Original line number Diff line number Diff line
@@ -1053,7 +1053,8 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
                dataCallSession.bandAtEnd,
                dataCallSession.handoverFailureCauses,
                dataCallSession.handoverFailureRat,
                dataCallSession.isNonDds);
                dataCallSession.isNonDds,
                dataCallSession.isIwlanCrossSim);
    }

    private static StatsEvent buildStatsEvent(ImsRegistrationStats stats) {
+322 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.telephony.DataFailCause;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
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.DataCallSession;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;

public class DataCallSessionStatsTest extends TelephonyTest {

    private ArgumentCaptor<NetworkCallback> mNetworkCallbackCaptor =
            ArgumentCaptor.forClass(NetworkCallback.class);
    private DataCallResponse mDefaultImsResponse = buildDataCallResponse("ims", 0);
    private Network mMockNetwork;
    private NetworkCapabilities mCellularNetworkCapabilities = new NetworkCapabilities.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).build();

    private static class TestableDataCallSessionStats extends DataCallSessionStats {
        private long mTimeMillis = 0L;

        TestableDataCallSessionStats(Phone phone) {
            super(phone);
        }

        @Override
        protected long getTimeMillis() {
            return mTimeMillis;
        }

        private void setTimeMillis(long timeMillis) {
            mTimeMillis = timeMillis;
        }
    }

    private TestableDataCallSessionStats mDataCallSessionStats;

    @Before
    public void setUp() throws Exception {
        super.setUp(getClass().getSimpleName());
        doNothing().when(mConnectivityManager).registerSystemDefaultNetworkCallback(
                mNetworkCallbackCaptor.capture(), any());
        mMockNetwork = mock(Network.class);
        when(mServiceState.getDataRegistrationState()).thenReturn(ServiceState.STATE_IN_SERVICE);
        mDataCallSessionStats = new TestableDataCallSessionStats(mPhone);
    }

    @After
    public void tearDown() throws Exception {
        mDataCallSessionStats = null;
        super.tearDown();
    }

    private NetworkCallback getNetworkMonitorCallback() {
        return mNetworkCallbackCaptor.getValue();
    }

    private DataCallResponse buildDataCallResponse(String apn, long retryDurationMillis) {
        return new DataCallResponse.Builder()
                .setId(apn.hashCode())
                .setRetryDurationMillis(retryDurationMillis)
                .build();
    }

    @Test
    @SmallTest
    public void testSetupDataCallOnCellularIms_success() {
        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
        mDataCallSessionStats.onSetupDataCallResponse(
                mDefaultImsResponse,
                TelephonyManager.NETWORK_TYPE_LTE,
                ApnSetting.TYPE_IMS,
                ApnSetting.PROTOCOL_IP,
                DataFailCause.NONE);

        mDataCallSessionStats.setTimeMillis(60000L);
        mDataCallSessionStats.conclude();

        ArgumentCaptor<DataCallSession> callCaptor =
                ArgumentCaptor.forClass(DataCallSession.class);
        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
                callCaptor.capture());
        DataCallSession stats = callCaptor.getValue();

        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
        assertEquals(1, stats.durationMinutes);
        assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.ratAtEnd);
        assertTrue(stats.ongoing);
    }

    @Test
    @SmallTest
    public void testSetupDataCallOnIwlan_success() {
        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
        mDataCallSessionStats.onSetupDataCallResponse(
                mDefaultImsResponse,
                TelephonyManager.NETWORK_TYPE_IWLAN,
                ApnSetting.TYPE_IMS,
                ApnSetting.PROTOCOL_IP,
                DataFailCause.NONE);

        mDataCallSessionStats.setTimeMillis(120000L);
        mDataCallSessionStats.conclude();

        ArgumentCaptor<DataCallSession> callCaptor =
                ArgumentCaptor.forClass(DataCallSession.class);
        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
                callCaptor.capture());
        DataCallSession stats = callCaptor.getValue();

        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
        assertEquals(2, stats.durationMinutes);
        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.ratAtEnd);
        assertFalse(stats.isIwlanCrossSim);
        assertTrue(stats.ongoing);
    }

    @Test
    @SmallTest
    public void testSetupDataCallOnCrossSimCalling_success() {
        getNetworkMonitorCallback().onAvailable(mMockNetwork);
        getNetworkMonitorCallback().onCapabilitiesChanged(
                mMockNetwork, mCellularNetworkCapabilities);
        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
        mDataCallSessionStats.onSetupDataCallResponse(
                mDefaultImsResponse,
                TelephonyManager.NETWORK_TYPE_IWLAN,
                ApnSetting.TYPE_IMS,
                ApnSetting.PROTOCOL_IP,
                DataFailCause.NONE);

        mDataCallSessionStats.setTimeMillis(60000L);
        mDataCallSessionStats.conclude();

        ArgumentCaptor<DataCallSession> callCaptor =
                ArgumentCaptor.forClass(DataCallSession.class);
        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
                callCaptor.capture());
        DataCallSession stats = callCaptor.getValue();

        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
        assertEquals(1, stats.durationMinutes);
        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.ratAtEnd);
        assertTrue(stats.isIwlanCrossSim);
        assertTrue(stats.ongoing);
    }

    @Test
    @SmallTest
    public void testSetupDataCallOnCellularIms_failure() {
        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
        mDataCallSessionStats.onSetupDataCallResponse(
                mDefaultImsResponse,
                TelephonyManager.NETWORK_TYPE_LTE,
                ApnSetting.TYPE_IMS,
                ApnSetting.PROTOCOL_IP,
                DataFailCause.NETWORK_FAILURE);

        ArgumentCaptor<DataCallSession> callCaptor =
                ArgumentCaptor.forClass(DataCallSession.class);
        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
                callCaptor.capture());
        DataCallSession stats = callCaptor.getValue();

        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
        assertEquals(0, stats.durationMinutes);
        assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.ratAtEnd);
        assertFalse(stats.ongoing);
    }

    @Test
    @SmallTest
    public void testHandoverFromCellularToIwlan_success() {
        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
        mDataCallSessionStats.onSetupDataCallResponse(
                mDefaultImsResponse,
                TelephonyManager.NETWORK_TYPE_LTE,
                ApnSetting.TYPE_IMS,
                ApnSetting.PROTOCOL_IP,
                DataFailCause.NONE);

        mDataCallSessionStats.onDrsOrRatChanged(TelephonyManager.NETWORK_TYPE_IWLAN);
        mDataCallSessionStats.conclude();

        ArgumentCaptor<DataCallSession> callCaptor =
                ArgumentCaptor.forClass(DataCallSession.class);
        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
                callCaptor.capture());
        DataCallSession stats = callCaptor.getValue();

        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.ratAtEnd);
        assertEquals(1, stats.ratSwitchCount);
        assertTrue(stats.ongoing);
    }

    @Test
    @SmallTest
    public void testHandoverFromCellularToCrossSimCalling_success() {
        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
        mDataCallSessionStats.onSetupDataCallResponse(
                mDefaultImsResponse,
                TelephonyManager.NETWORK_TYPE_LTE,
                ApnSetting.TYPE_IMS,
                ApnSetting.PROTOCOL_IP,
                DataFailCause.NONE);

        getNetworkMonitorCallback().onAvailable(mMockNetwork);
        getNetworkMonitorCallback().onCapabilitiesChanged(
                mMockNetwork, mCellularNetworkCapabilities);
        mDataCallSessionStats.onDrsOrRatChanged(TelephonyManager.NETWORK_TYPE_IWLAN);
        mDataCallSessionStats.conclude();

        ArgumentCaptor<DataCallSession> callCaptor =
                ArgumentCaptor.forClass(DataCallSession.class);
        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
                callCaptor.capture());
        DataCallSession stats = callCaptor.getValue();

        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.ratAtEnd);
        assertEquals(1, stats.ratSwitchCount);
        assertTrue(stats.isIwlanCrossSim);
        assertTrue(stats.ongoing);
    }

    @Test
    @SmallTest
    public void testHandoverFromCellularToIwlan_failure() {
        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
        mDataCallSessionStats.onSetupDataCallResponse(
                mDefaultImsResponse,
                TelephonyManager.NETWORK_TYPE_LTE,
                ApnSetting.TYPE_IMS,
                ApnSetting.PROTOCOL_IP,
                DataFailCause.NONE);

        mDataCallSessionStats.onHandoverFailure(DataFailCause.IWLAN_DNS_RESOLUTION_TIMEOUT,
                TelephonyManager.NETWORK_TYPE_LTE, TelephonyManager.NETWORK_TYPE_IWLAN);
        mDataCallSessionStats.conclude();

        ArgumentCaptor<DataCallSession> callCaptor =
                ArgumentCaptor.forClass(DataCallSession.class);
        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
                callCaptor.capture());
        DataCallSession stats = callCaptor.getValue();

        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
        assertEquals(TelephonyManager.NETWORK_TYPE_LTE, stats.ratAtEnd);
        assertTrue(stats.ongoing);
        assertEquals(DataFailCause.IWLAN_DNS_RESOLUTION_TIMEOUT,
                stats.handoverFailureCauses[0]);

        int cellularToIwlanFailureDirection = TelephonyManager.NETWORK_TYPE_LTE
                | (TelephonyManager.NETWORK_TYPE_IWLAN << 16);
        assertEquals(cellularToIwlanFailureDirection, stats.handoverFailureRat[0]);
    }

    @Test
    @SmallTest
    public void testSetupDataCallOnIwlan_success_thenOOS() {
        mDataCallSessionStats.onSetupDataCall(ApnSetting.TYPE_IMS);
        mDataCallSessionStats.onSetupDataCallResponse(
                mDefaultImsResponse,
                TelephonyManager.NETWORK_TYPE_IWLAN,
                ApnSetting.TYPE_IMS,
                ApnSetting.PROTOCOL_IP,
                DataFailCause.NONE);
        when(mServiceState.getDataRegistrationState())
                .thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
        mDataCallSessionStats.onDataCallDisconnected(DataFailCause.IWLAN_IKE_DPD_TIMEOUT);

        ArgumentCaptor<DataCallSession> callCaptor =
                ArgumentCaptor.forClass(DataCallSession.class);
        verify(mPersistAtomsStorage, times(1)).addDataCallSession(
                callCaptor.capture());
        DataCallSession stats = callCaptor.getValue();

        assertEquals(ApnSetting.TYPE_IMS, stats.apnTypeBitmask);
        assertEquals(TelephonyManager.NETWORK_TYPE_IWLAN, stats.ratAtEnd);
        assertTrue(stats.oosAtEnd);
        assertFalse(stats.ongoing);
    }
}