Loading src/java/com/android/internal/telephony/RIL.java +4 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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); Loading Loading @@ -7548,7 +7548,7 @@ public class RIL extends BaseCommands implements CommandsInterface { .setHandoverFailureMode(handoverFailureMode) .setPduSessionId(pduSessionId) .setDefaultQos(defaultQos) .setQosSessions(qosSessions) .setQosBearerSessions(qosSessions) .setSliceInfo(sliceInfo) .build(); } Loading src/java/com/android/internal/telephony/dataconnection/DataConnection.java +32 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -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); } /** Loading Loading @@ -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); Loading Loading @@ -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); } Loading Loading @@ -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. Loading Loading @@ -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); Loading src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java +42 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading @@ -68,7 +75,7 @@ public class DcNetworkAgent extends NetworkAgent { private final int mId; private Phone mPhone; private final Phone mPhone; private int mTransportType; Loading @@ -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); Loading Loading @@ -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-" Loading src/java/com/android/internal/telephony/dataconnection/QosCallbackTracker.java 0 → 100644 +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); } } tests/telephonytests/src/com/android/internal/telephony/RILTest.java +12 −12 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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)); Loading Loading @@ -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)); Loading Loading @@ -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() Loading @@ -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 Loading
src/java/com/android/internal/telephony/RIL.java +4 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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); Loading Loading @@ -7548,7 +7548,7 @@ public class RIL extends BaseCommands implements CommandsInterface { .setHandoverFailureMode(handoverFailureMode) .setPduSessionId(pduSessionId) .setDefaultQos(defaultQos) .setQosSessions(qosSessions) .setQosBearerSessions(qosSessions) .setSliceInfo(sliceInfo) .build(); } Loading
src/java/com/android/internal/telephony/dataconnection/DataConnection.java +32 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -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); } /** Loading Loading @@ -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); Loading Loading @@ -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); } Loading Loading @@ -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. Loading Loading @@ -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); Loading
src/java/com/android/internal/telephony/dataconnection/DcNetworkAgent.java +42 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading @@ -68,7 +75,7 @@ public class DcNetworkAgent extends NetworkAgent { private final int mId; private Phone mPhone; private final Phone mPhone; private int mTransportType; Loading @@ -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); Loading Loading @@ -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-" Loading
src/java/com/android/internal/telephony/dataconnection/QosCallbackTracker.java 0 → 100644 +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); } }
tests/telephonytests/src/com/android/internal/telephony/RILTest.java +12 −12 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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)); Loading Loading @@ -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)); Loading Loading @@ -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() Loading @@ -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