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

Commit a8b11fb6 authored by Jayachandran Chinnakkannu's avatar Jayachandran Chinnakkannu Committed by Android (Google) Code Review
Browse files

Merge "Add support for Qos filter matching" into sc-dev

parents 4fa964b1 ad280950
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -107,7 +107,7 @@ import android.telephony.data.DataCallResponse.HandoverFailureMode;
import android.telephony.data.DataProfile;
import android.telephony.data.DataService;
import android.telephony.data.Qos;
import android.telephony.data.QosSession;
import android.telephony.data.QosBearerSession;
import android.telephony.data.SliceInfo;
import android.telephony.emergency.EmergencyNumber;
import android.text.TextUtils;
@@ -7389,7 +7389,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
        int pduSessionId = DataCallResponse.PDU_SESSION_ID_NOT_SET;

        List<LinkAddress> laList = new ArrayList<>();
        List<QosSession> qosSessions = new ArrayList<>();
        List<QosBearerSession> qosSessions = new ArrayList<>();
        SliceInfo sliceInfo = null;

        if (dcResult instanceof android.hardware.radio.V1_0.SetupDataCallResult) {
@@ -7479,7 +7479,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
            pduSessionId = result.pduSessionId;
            defaultQos = Qos.create(result.defaultQos);
            qosSessions = result.qosSessions.stream().map(session ->
                    QosSession.create(session)).collect(Collectors.toList());
                    QosBearerSession.create(session)).collect(Collectors.toList());
            sliceInfo = convertToSliceInfo(result.sliceInfo);
        } else {
            Rlog.e(RILJ_LOG_TAG, "Unsupported SetupDataCallResult " + dcResult);
@@ -7548,7 +7548,7 @@ public class RIL extends BaseCommands implements CommandsInterface {
                .setHandoverFailureMode(handoverFailureMode)
                .setPduSessionId(pduSessionId)
                .setDefaultQos(defaultQos)
                .setQosSessions(qosSessions)
                .setQosBearerSessions(qosSessions)
                .setSliceInfo(sliceInfo)
                .build();
    }
+32 −5
Original line number Diff line number Diff line
@@ -67,7 +67,7 @@ import android.telephony.data.DataProfile;
import android.telephony.data.DataService;
import android.telephony.data.DataServiceCallback;
import android.telephony.data.Qos;
import android.telephony.data.QosSession;
import android.telephony.data.QosBearerSession;
import android.telephony.data.SliceInfo;
import android.text.TextUtils;
import android.util.LocalLog;
@@ -305,7 +305,7 @@ public class DataConnection extends StateMachine {
    private int mDownlinkBandwidth = 14;
    private int mUplinkBandwidth = 14;
    private Qos mDefaultQos = null;
    private List<QosSession> mQosSessions = new ArrayList<>();
    private List<QosBearerSession> mQosBearerSessions = new ArrayList<>();
    private SliceInfo mSliceInfo;

    /** The corresponding network agent for this data connection. */
@@ -618,9 +618,29 @@ public class DataConnection extends StateMachine {
        return mSliceInfo;
    }

    public void updateQosParameters(DataCallResponse response) {
    public void updateQosParameters(final @Nullable DataCallResponse response) {
        if (response == null) {
            mDefaultQos = null;
            mQosBearerSessions = null;
            return;
        }

        mDefaultQos = response.getDefaultQos();
        mQosSessions = response.getQosSessions();
        mQosBearerSessions = response.getQosBearerSessions();

        if (mNetworkAgent != null) {
            syncQosToNetworkAgent();
        }
    }

    private void syncQosToNetworkAgent() {
        final DcNetworkAgent networkAgent = mNetworkAgent;
        final List<QosBearerSession> qosBearerSessions = mQosBearerSessions;
        if (qosBearerSessions == null) {
            networkAgent.updateQosBearerSessions(new ArrayList<>());
            return;
        }
        networkAgent.updateQosBearerSessions(qosBearerSessions);
    }

    /**
@@ -2695,6 +2715,9 @@ public class DataConnection extends StateMachine {
                sendMessage(obtainMessage(EVENT_UPDATE_SUSPENDED_STATE));
            }

            // The qos parameters are set when the call is connected
            syncQosToNetworkAgent();

            if (mTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
                mPhone.mCi.registerForNattKeepaliveStatus(
                        getHandler(), DataConnection.EVENT_KEEPALIVE_STATUS, null);
@@ -2723,6 +2746,7 @@ public class DataConnection extends StateMachine {
            // which is when IWLAN handover is ongoing. Instead of unregistering, the agent will
            // be transferred to the new data connection on the other transport.
            if (mNetworkAgent != null) {
                syncQosToNetworkAgent();
                if (mHandoverState == HANDOVER_STATE_IDLE) {
                    mNetworkAgent.unregister(DataConnection.this);
                }
@@ -3146,6 +3170,9 @@ public class DataConnection extends StateMachine {
                    if (DBG) log(str);
                    if (dp.mApnContext != null) dp.mApnContext.requestLog(str);

                    // Clear out existing qos sessions
                    updateQosParameters(null);

                    if (dp.mTag == mTag) {
                        // Transition to inactive but send notifications after
                        // we've entered the mInactive state.
@@ -3719,7 +3746,7 @@ public class DataConnection extends StateMachine {
        pw.println("mDownlinkBandwidth" + mDownlinkBandwidth);
        pw.println("mUplinkBandwidth=" + mUplinkBandwidth);
        pw.println("mDefaultQos=" + mDefaultQos);
        pw.println("mQosSessions=" + mQosSessions);
        pw.println("mQosBearerSessions=" + mQosBearerSessions);
        pw.println("disallowedApnTypes="
                + ApnSetting.getApnTypesStringFromBitmask(getDisallowedApnTypes()));
        pw.println("mInstanceNumber=" + mInstanceNumber);
+42 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.net.NetworkAgent;
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkProvider;
import android.net.QosFilter;
import android.net.SocketKeepalive;
import android.net.Uri;
import android.os.Message;
@@ -35,6 +36,8 @@ import android.telephony.AnomalyReporter;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.data.EpsBearerQosSessionAttributes;
import android.telephony.data.QosBearerSession;
import android.util.ArrayMap;
import android.util.LocalLog;
import android.util.SparseArray;
@@ -48,10 +51,14 @@ import com.android.telephony.Rlog;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * This class represents a network agent which is communication channel between
@@ -68,7 +75,7 @@ public class DcNetworkAgent extends NetworkAgent {

    private final int mId;

    private Phone mPhone;
    private final Phone mPhone;

    private int mTransportType;

@@ -76,6 +83,10 @@ public class DcNetworkAgent extends NetworkAgent {

    public final DcKeepaliveTracker keepaliveTracker = new DcKeepaliveTracker();

    private final QosCallbackTracker mQosCallbackTracker = new QosCallbackTracker(this);

    private final Executor mQosCallbackExecutor = Executors.newSingleThreadExecutor();

    private DataConnection mDataConnection;

    private final LocalLog mNetCapsLocalLog = new LocalLog(50);
@@ -345,6 +356,36 @@ public class DcNetworkAgent extends NetworkAgent {
                .sendToTarget();
    }

    @Override
    public void onQosCallbackRegistered(final int qosCallbackId, final @NonNull QosFilter filter) {
        mQosCallbackExecutor.execute(() -> mQosCallbackTracker.addFilter(qosCallbackId,
              new QosCallbackTracker.IFilter() {
                  @Override
                  public boolean matchesLocalAddress(
                          InetAddress address, int startPort, int endPort) {
                      return filter.matchesLocalAddress(address, startPort, endPort);
                  }
              }));
    }

    @Override
    public void onQosCallbackUnregistered(final int qosCallbackId) {
        mQosCallbackExecutor.execute(() -> mQosCallbackTracker.removeFilter(qosCallbackId));
    }

    void updateQosBearerSessions(final List<QosBearerSession> qosBearerSessions) {
        mQosCallbackExecutor.execute(() -> mQosCallbackTracker.updateSessions(qosBearerSessions));
    }

    public void notifyQosSessionAvailable(final int qosCallbackId, final int sessionId,
            @NonNull final EpsBearerQosSessionAttributes attributes) {
        super.sendQosSessionAvailable(qosCallbackId, sessionId, attributes);
    }

    public void notifyQosSessionLost(final int qosCallbackId, final int sessionId) {
        super.sendQosSessionLost(qosCallbackId, sessionId);
    }

    @Override
    public String toString() {
        return "DcNetworkAgent-"
+215 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.dataconnection;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.LinkAddress;
import android.telephony.data.EpsQos;
import android.telephony.data.EpsBearerQosSessionAttributes;
import android.telephony.data.QosBearerFilter;
import android.telephony.data.QosBearerSession;

import com.android.telephony.Rlog;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Matches filters with qos sessions and send corresponding available and lost events.
 *
 * Note: This class is <b>NOT</b> thread-safe
 *
 * {@hide}
 */
public class QosCallbackTracker {
    private static final String LOG_TAG = QosCallbackTracker.class.getSimpleName();
    @NonNull private final DcNetworkAgent mDcNetworkAgent;
    @NonNull private final Map<Integer, QosBearerSession> mQosBearerSessions;

    // We perform an exact match on the address
    @NonNull private final Map<Integer, IFilter> mCallbacksToFilter;

    /**
     * Construct a new tracker
     * @param dcNetworkAgent the network agent to send events to
     */
    public QosCallbackTracker(@NonNull final DcNetworkAgent dcNetworkAgent) {
        mQosBearerSessions = new HashMap<>();
        mCallbacksToFilter = new HashMap<>();
        mDcNetworkAgent = dcNetworkAgent;
    }

    /**
     * Add new filter that is to receive events
     *
     * @param callbackId the associated callback id
     * @param filter provides the matching logic
     */
    public void addFilter(final int callbackId, final IFilter filter) {
        logd("addFilter: callbackId=" + callbackId);
        // Called from mDcNetworkAgent
        mCallbacksToFilter.put(callbackId, filter);

        //On first change. Check all sessions and send.
        for (final QosBearerSession session : mQosBearerSessions.values()) {
            if (doFiltersMatch(session, filter)) {
                sendSessionAvailable(callbackId, session, filter);
            }
        }
    }

    /**
     * Remove the filter with the associated callback id
     *
     * @param callbackId the qos callback id
     */
    public void removeFilter(final int callbackId) {
        logd("removeFilter: callbackId=" + callbackId);
        mCallbacksToFilter.remove(callbackId);
    }

    /**
     * Update the list of qos sessions and send out corresponding events
     *
     * @param sessions the new list of qos sessions
     */
    public void updateSessions(@NonNull final List<QosBearerSession> sessions) {
        logd("updateSessions: sessions size=" + sessions.size());
        final List<QosBearerSession> sessionsToAdd = new ArrayList<>();
        final Map<Integer, QosBearerSession> incomingSessions = new HashMap<>();
        for (final QosBearerSession incomingSession : sessions) {
            incomingSessions.put(incomingSession.getQosBearerSessionId(), incomingSession);

            final QosBearerSession existingSession = mQosBearerSessions.get(
                    incomingSession.getQosBearerSessionId());
            for (final int callbackId : mCallbacksToFilter.keySet()) {
                final IFilter filter = mCallbacksToFilter.get(callbackId);

                final boolean incomingSessionMatch = doFiltersMatch(incomingSession, filter);
                final boolean existingSessionMatch =
                        existingSession != null && doFiltersMatch(existingSession, filter);

                if (!existingSessionMatch && incomingSessionMatch) {
                    // The filter matches now and didn't match earlier
                    sendSessionAvailable(callbackId, incomingSession, filter);
                }

                if (existingSessionMatch && incomingSessionMatch) {
                    // The same sessions matches the same filter, but if the qos changed,
                    // the callback still needs to be notified
                    if (!incomingSession.getQos().equals(existingSession.getQos())) {
                        sendSessionAvailable(callbackId, incomingSession, filter);
                    }
                }
            }
            sessionsToAdd.add(incomingSession);
        }

        final List<Integer> sessionsToRemove = new ArrayList<>();
        // Find sessions that no longer exist
        for (final QosBearerSession existingSession : mQosBearerSessions.values()) {
            if (!incomingSessions.containsKey(existingSession.getQosBearerSessionId())) {
                for (final int callbackId : mCallbacksToFilter.keySet()) {
                    final IFilter filter = mCallbacksToFilter.get(callbackId);
                    // The filter matches which means it was previously available, and now is lost
                    if (doFiltersMatch(existingSession, filter)) {
                        sendSessionLost(callbackId, existingSession);
                    }
                }
                sessionsToRemove.add(existingSession.getQosBearerSessionId());
            }
        }

        // Add in the new or existing sessions with updated information
        for (final QosBearerSession sessionToAdd : sessionsToAdd) {
            mQosBearerSessions.put(sessionToAdd.getQosBearerSessionId(), sessionToAdd);
        }

        // Remove any old sessions
        for (final int sessionToRemove : sessionsToRemove) {
            mQosBearerSessions.remove(sessionToRemove);
        }
    }

    private boolean doFiltersMatch(
            final QosBearerSession qosBearerSession, final IFilter filter) {
        return getMatchingQosBearerFilter(qosBearerSession, filter) != null;
    }

    private QosBearerFilter getMatchingQosBearerFilter(
            final QosBearerSession qosBearerSession, final IFilter filter) {
        QosBearerFilter qosFilter = null;

        for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) {
            for (final LinkAddress qosAddress : sessionFilter.getLocalAddresses()) {
                if (filter.matchesLocalAddress(qosAddress.getAddress(),
                        sessionFilter.getLocalPortRange().getStart(),
                        sessionFilter.getLocalPortRange().getEnd())) {
                    // Find for the highest precedence filter
                    if (qosFilter == null
                            || sessionFilter.getPrecedence() < qosFilter.getPrecedence()) {
                        qosFilter = sessionFilter;
                    }
                }
            }
        }
        return qosFilter;
    }

    private void sendSessionAvailable(final int callbackId,
            @NonNull final QosBearerSession session, @NonNull IFilter filter) {
        QosBearerFilter qosBearerFilter = getMatchingQosBearerFilter(session, filter);
        List<InetSocketAddress> remoteAddresses = new ArrayList<>();
        EpsQos qos = (EpsQos) session.getQos();
        if(qosBearerFilter.getRemoteAddresses().size() > 0) {
            remoteAddresses.add(
                  new InetSocketAddress(qosBearerFilter.getRemoteAddresses().get(0).getAddress(),
                  qosBearerFilter.getRemotePortRange().getStart()));
        }
        EpsBearerQosSessionAttributes epsBearerAttr =
                new EpsBearerQosSessionAttributes(qos.getQci(),
                        qos.getUplinkBandwidth().getMaxBitrateKbps(),
                        qos.getDownlinkBandwidth().getMaxBitrateKbps(),
                        qos.getDownlinkBandwidth().getGuaranteedBitrateKbps(),
                        qos.getUplinkBandwidth().getGuaranteedBitrateKbps(),
                        remoteAddresses);
        mDcNetworkAgent.notifyQosSessionAvailable(
                callbackId, session.getQosBearerSessionId(), epsBearerAttr);
    }

    private void sendSessionLost(final int callbackId, @NonNull final QosBearerSession session) {
        mDcNetworkAgent.notifyQosSessionLost(callbackId, session.getQosBearerSessionId());
    }

    public interface IFilter {
        public boolean matchesLocalAddress(InetAddress address, int startPort, int endPort);
    }

    /**
     * Log with debug level
     *
     * @param s is string log
     */
    private void logd(String s) {
        Rlog.d(LOG_TAG, s);
    }
}
+12 −12
Original line number Diff line number Diff line
@@ -158,8 +158,8 @@ import android.telephony.data.ApnSetting;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataProfile;
import android.telephony.data.EpsQos;
import android.telephony.data.QosFilter;
import android.telephony.data.QosSession;
import android.telephony.data.QosBearerFilter;
import android.telephony.data.QosBearerSession;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;

@@ -2180,7 +2180,7 @@ public class RILTest extends TelephonyTest {
                .setMtu(1500)
                .setMtuV4(1500)
                .setMtuV6(1500)
                .setQosSessions(new ArrayList<>())
                .setQosBearerSessions(new ArrayList<>())
                .build();

        assertEquals(response, RIL.convertDataCallResult(result10));
@@ -2254,7 +2254,7 @@ public class RILTest extends TelephonyTest {
                .setMtu(3000)
                .setMtuV4(1500)
                .setMtuV6(3000)
                .setQosSessions(new ArrayList<>())
                .setQosBearerSessions(new ArrayList<>())
                .build();

        assertEquals(response, RIL.convertDataCallResult(result15));
@@ -2322,18 +2322,18 @@ public class RILTest extends TelephonyTest {
        result16.qosSessions = new ArrayList<>(Arrays.asList(halQosSession));

        EpsQos epsQos = new EpsQos(halEpsQos);
        QosFilter qosFilter = new QosFilter(
        QosBearerFilter qosFilter = new QosBearerFilter(
                Arrays.asList(
                        new LinkAddress(InetAddresses.parseNumericAddress("122.22.22.22"), 32)),
                Arrays.asList(
                        new LinkAddress(InetAddresses.parseNumericAddress("144.44.44.44"), 32)),
                new QosFilter.PortRange(123, 123), new QosFilter.PortRange(223, 223),
                QosFilter.QOS_PROTOCOL_UDP, 7, 987, 678,
                QosFilter.QOS_FILTER_DIRECTION_BIDIRECTIONAL, 45);
        ArrayList<QosFilter> qosFilters = new ArrayList<>();
        ArrayList<QosSession> qosSessions = new ArrayList<>();
                new QosBearerFilter.PortRange(123, 123), new QosBearerFilter.PortRange(223, 223),
                QosBearerFilter.QOS_PROTOCOL_UDP, 7, 987, 678,
                QosBearerFilter.QOS_FILTER_DIRECTION_BIDIRECTIONAL, 45);
        ArrayList<QosBearerFilter> qosFilters = new ArrayList<>();
        ArrayList<QosBearerSession> qosSessions = new ArrayList<>();
        qosFilters.add(qosFilter);
        QosSession qosSession = new QosSession(1234, epsQos, qosFilters);
        QosBearerSession qosSession = new QosBearerSession(1234, epsQos, qosFilters);
        qosSessions.add(qosSession);

        response = new DataCallResponse.Builder()
@@ -2359,7 +2359,7 @@ public class RILTest extends TelephonyTest {
                .setMtuV6(3000)
                .setHandoverFailureMode(DataCallResponse.HANDOVER_FAILURE_MODE_LEGACY)
                .setDefaultQos(epsQos)
                .setQosSessions(qosSessions)
                .setQosBearerSessions(qosSessions)
                .build();

        assertEquals(response, RIL.convertDataCallResult(result16));
Loading