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

Commit 9431a8b5 authored by Jaesik Kong's avatar Jaesik Kong Committed by Android (Google) Code Review
Browse files

Merge changes from topics "handover_in_progress", "voice_rat_switch_count" into udc-qpr-dev

* changes:
  Add handover_in_progress to VoiceCallSessionStats
  Add rat_switch_count_after_connected to VoiceCallSessionStats
parents 634c5f86 139f49d9
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -275,6 +275,8 @@ message VoiceCallSession {
    optional int32 call_duration = 32;
    optional int32 last_known_rat = 33;
    optional int32 fold_state = 34;
    optional int64 rat_switch_count_after_connected = 35;
    optional bool handover_in_progress = 36;

    // Internal use only
    optional int64 setup_begin_millis = 10001;
+189 −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 android.os.Handler;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.telephony.PreciseDataConnectionState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Phone;

import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Executor;

/**
 *  This Tracker is monitoring precise data connection states for each APNs which are used for IMS
 * calling such as IMS and Emergency APN. It uses a SparseArray to track each SIM's connection
 * state.
 *  The tracker is started by {@link VoiceCallSessionStats} and update the states to
 * VoiceCallSessionStats directly.
 */
public class DataConnectionStateTracker {
    private static final SparseArray<DataConnectionStateTracker> sDataConnectionStateTracker =
            new SparseArray<>();
    private final Executor mExecutor;
    private Phone mPhone;
    private int mSubId;
    private HashMap<Integer, PreciseDataConnectionState> mLastPreciseDataConnectionState =
            new HashMap<>();
    private PreciseDataConnectionStateListenerImpl mDataConnectionStateListener;

    private final SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener =
            new SubscriptionManager.OnSubscriptionsChangedListener() {
                @Override
                public void onSubscriptionsChanged() {
                    if (mPhone == null) {
                        return;
                    }
                    int newSubId = mPhone.getSubId();
                    if (mSubId == newSubId
                            || newSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
                        return;
                    }

                    unregisterTelephonyListener();
                    mSubId = newSubId;
                    registerTelephonyListener(mSubId);
                }
            };

    private DataConnectionStateTracker() {
        HandlerThread handlerThread =
                new HandlerThread(DataConnectionStateTracker.class.getSimpleName());
        handlerThread.start();
        mExecutor = new HandlerExecutor(new Handler(handlerThread.getLooper()));
    }

    /** Getting or Creating DataConnectionStateTracker based on phoneId */
    public static synchronized DataConnectionStateTracker getInstance(int phoneId) {
        DataConnectionStateTracker dataConnectionStateTracker =
                sDataConnectionStateTracker.get(phoneId);
        if (dataConnectionStateTracker != null) {
            return dataConnectionStateTracker;
        }

        dataConnectionStateTracker = new DataConnectionStateTracker();
        sDataConnectionStateTracker.put(phoneId, dataConnectionStateTracker);
        return dataConnectionStateTracker;
    }

    /** Starting to monitor the precise data connection states */
    public void start(Phone phone) {
        mPhone = phone;
        mSubId = mPhone.getSubId();
        registerTelephonyListener(mSubId);
        SubscriptionManager mSubscriptionManager = mPhone.getContext()
                .getSystemService(SubscriptionManager.class);
        if (mSubscriptionManager != null) {
            mSubscriptionManager
                    .addOnSubscriptionsChangedListener(mExecutor, mSubscriptionsChangedListener);
        }
    }

    /** Stopping monitoring for the precise data connection states */
    public void stop() {
        if (mPhone == null) {
            return;
        }
        SubscriptionManager mSubscriptionManager = mPhone.getContext()
                .getSystemService(SubscriptionManager.class);
        if (mSubscriptionManager != null) {
            mSubscriptionManager
                    .removeOnSubscriptionsChangedListener(mSubscriptionsChangedListener);
        }
        unregisterTelephonyListener();
        mPhone = null;
        mLastPreciseDataConnectionState.clear();
    }

    /** Returns data state of the last notified precise data connection state for apn type */
    public int getDataState(int apnType) {
        if (!mLastPreciseDataConnectionState.containsKey(apnType)) {
            return TelephonyManager.DATA_UNKNOWN;
        }
        return mLastPreciseDataConnectionState.get(apnType).getState();
    }

    private void registerTelephonyListener(int subId) {
        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
            return;
        }
        TelephonyManager telephonyManager =
                mPhone.getContext().getSystemService(TelephonyManager.class);
        if (telephonyManager != null) {
            mDataConnectionStateListener = new PreciseDataConnectionStateListenerImpl(mExecutor);
            mDataConnectionStateListener.register(telephonyManager.createForSubscriptionId(subId));
        }
    }

    private void unregisterTelephonyListener() {
        if (mDataConnectionStateListener != null) {
            mDataConnectionStateListener.unregister();
            mDataConnectionStateListener = null;
        }
    }

    @VisibleForTesting
    public void notifyDataConnectionStateChanged(PreciseDataConnectionState connectionState) {
        List<Integer> apnTypes = connectionState.getApnSetting().getApnTypes();
        if (apnTypes != null) {
            for (int apnType : apnTypes) {
                mLastPreciseDataConnectionState.put(apnType, connectionState);
            }
        }

        mPhone.getVoiceCallSessionStats().onPreciseDataConnectionStateChanged(connectionState);
    }

    private class PreciseDataConnectionStateListenerImpl extends TelephonyCallback
            implements TelephonyCallback.PreciseDataConnectionStateListener {
        private final Executor mExecutor;
        private TelephonyManager mTelephonyManager = null;

        PreciseDataConnectionStateListenerImpl(Executor executor) {
            mExecutor = executor;
        }

        public void register(TelephonyManager tm) {
            if (tm == null) {
                return;
            }
            mTelephonyManager = tm;
            mTelephonyManager.registerTelephonyCallback(mExecutor, this);
        }

        public void unregister() {
            if (mTelephonyManager != null) {
                mTelephonyManager.unregisterTelephonyCallback(this);
                mTelephonyManager = null;
            }
        }

        @Override
        public void onPreciseDataConnectionStateChanged(
                PreciseDataConnectionState connectionState) {
            notifyDataConnectionStateChanged(connectionState);
        }
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -976,7 +976,9 @@ public class MetricsCollector implements StatsManager.StatsPullAtomCallback {
                session.isMultiparty,
                session.callDuration,
                session.lastKnownRat,
                session.foldState);
                session.foldState,
                session.ratSwitchCountAfterConnected,
                session.handoverInProgress);
    }

    private static StatsEvent buildStatsEvent(IncomingSms sms) {
+64 −0
Original line number Diff line number Diff line
@@ -47,8 +47,10 @@ import android.telephony.Annotation.NetworkType;
import android.telephony.AnomalyReporter;
import android.telephony.DisconnectCause;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.ims.ImsStreamMediaProfile;
import android.util.LongSparseArray;
@@ -163,6 +165,8 @@ public class VoiceCallSessionStats {
    public VoiceCallSessionStats(int phoneId, Phone phone) {
        mPhoneId = phoneId;
        mPhone = phone;

        DataConnectionStateTracker.getInstance(phoneId).start(phone);
    }

    /* CS calls */
@@ -395,6 +399,14 @@ public class VoiceCallSessionStats {
        }
    }

    /** Updates internal states when IMS/Emergency PDN/PDU state changes */
    public synchronized void onPreciseDataConnectionStateChanged(
            PreciseDataConnectionState connectionState) {
        if (hasCalls()) {
            updateVoiceCallSessionBearerState(connectionState);
        }
    }

    /* internal */

    /** Handles ringing MT call getting accepted. */
@@ -440,6 +452,7 @@ public class VoiceCallSessionStats {
        proto.ratAtConnected = TelephonyManager.NETWORK_TYPE_UNKNOWN;
        proto.ratAtEnd = rat;
        proto.ratSwitchCount = 0L;
        proto.ratSwitchCountAfterConnected = 0L;
        proto.codecBitmask = 0L;
        proto.simSlotIndex = mPhoneId;
        proto.isMultiSim = SimSlotState.isMultiSim();
@@ -454,6 +467,7 @@ public class VoiceCallSessionStats {
        proto.isMultiparty = conn.isMultiparty();
        proto.lastKnownRat = rat;
        proto.videoEnabled = videoState != VideoProfile.STATE_AUDIO_ONLY ? true : false;
        proto.handoverInProgress = isHandoverInProgress(bearer, proto.isEmergency);

        // internal fields for tracking
        if (getDirection(conn) == VOICE_CALL_SESSION__DIRECTION__CALL_DIRECTION_MT) {
@@ -609,6 +623,9 @@ public class VoiceCallSessionStats {
    private void updateRatAtEnd(VoiceCallSession proto, @NetworkType int rat) {
        if (proto.ratAtEnd != rat) {
            proto.ratSwitchCount++;
            if (!proto.setupFailed) {
                proto.ratSwitchCountAfterConnected++;
            }
            proto.ratAtEnd = rat;
            if (rat != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
                proto.lastKnownRat = rat;
@@ -682,6 +699,17 @@ public class VoiceCallSessionStats {
        return mPhone.getSignalStrength().getLevel();
    }

    private boolean isHandoverInProgress(int bearer, boolean isEmergency) {
        // If the call is not IMS, the bearer will not be able to handover
        if (bearer != VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) {
            return false;
        }

        int apnType = isEmergency ? ApnSetting.TYPE_EMERGENCY : ApnSetting.TYPE_IMS;
        int dataState = DataConnectionStateTracker.getInstance(mPhoneId).getDataState(apnType);
        return dataState == TelephonyManager.DATA_HANDOVER_IN_PROGRESS;
    }

    /**
     * This is a copy of ServiceStateStats.getVoiceRat(Phone, ServiceState, int) with minimum fix
     * required for tracking EPSFB correctly.
@@ -924,4 +952,40 @@ public class VoiceCallSessionStats {

        return map;
    }

    private void updateVoiceCallSessionBearerState(PreciseDataConnectionState connectionState) {
        ApnSetting apnSetting = connectionState.getApnSetting();
        if (apnSetting == null) {
            return;
        }

        int apnTypes = apnSetting.getApnTypeBitmask();
        if ((apnTypes & ApnSetting.TYPE_IMS) == 0
                && (apnTypes & ApnSetting.TYPE_EMERGENCY) == 0) {
            return;
        }

        for (int i = 0; i < mCallProtos.size(); i++) {
            VoiceCallSession proto = mCallProtos.valueAt(i);
            if (proto.bearerAtEnd == VOICE_CALL_SESSION__BEARER_AT_END__CALL_BEARER_IMS) {
                if (!proto.isEmergency && (apnTypes & ApnSetting.TYPE_IMS) != 0) {
                    updateHandoverState(proto, connectionState.getState());
                }
                if (proto.isEmergency && (apnTypes & ApnSetting.TYPE_EMERGENCY) != 0) {
                    updateHandoverState(proto, connectionState.getState());
                }
            }
        }
    }

    private void updateHandoverState(VoiceCallSession proto, int dataState) {
        switch (dataState) {
            case TelephonyManager.DATA_HANDOVER_IN_PROGRESS:
                proto.handoverInProgress = true;
                break;
            default:
                // All other states are considered as not in handover
                proto.handoverInProgress = false;
        }
    }
}
+360 −2

File changed.

Preview size limit exceeded, changes collapsed.