1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.telephony.data; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.net.LinkAddress; 22 import android.net.NetworkAgent; 23 import android.net.QosFilter; 24 import android.net.QosSession; 25 import android.os.Handler; 26 import android.telephony.TelephonyManager; 27 import android.telephony.data.EpsBearerQosSessionAttributes; 28 import android.telephony.data.EpsQos; 29 import android.telephony.data.NrQos; 30 import android.telephony.data.NrQosSessionAttributes; 31 import android.telephony.data.QosBearerFilter; 32 import android.telephony.data.QosBearerSession; 33 34 import com.android.internal.telephony.Phone; 35 import com.android.internal.telephony.metrics.RcsStats; 36 import com.android.telephony.Rlog; 37 38 import java.net.InetAddress; 39 import java.net.InetSocketAddress; 40 import java.util.ArrayList; 41 import java.util.HashMap; 42 import java.util.HashSet; 43 import java.util.List; 44 import java.util.Map; 45 46 /** 47 * Matches filters with qos sessions and send corresponding available and lost events. 48 */ 49 public class QosCallbackTracker extends Handler { 50 private static final int DEDICATED_BEARER_EVENT_STATE_NONE = 0; 51 private static final int DEDICATED_BEARER_EVENT_STATE_ADDED = 1; 52 private static final int DEDICATED_BEARER_EVENT_STATE_MODIFIED = 2; 53 private static final int DEDICATED_BEARER_EVENT_STATE_DELETED = 3; 54 55 private final @NonNull String mLogTag; 56 // TODO: Change this to TelephonyNetworkAgent 57 private final @NonNull NotifyQosSessionInterface mNetworkAgent; 58 private final @NonNull Map<Integer, QosBearerSession> mQosBearerSessions; 59 private final @NonNull RcsStats mRcsStats; 60 61 // We perform an exact match on the address 62 private final @NonNull Map<Integer, IFilter> mCallbacksToFilter; 63 64 private final int mPhoneId; 65 66 /** 67 * QOS sessions filter interface 68 */ 69 public interface IFilter { 70 /** 71 * Filter using the local address. 72 * 73 * @param address The local address. 74 * @param startPort Starting port. 75 * @param endPort Ending port. 76 * @return {@code true} if matches, {@code false} otherwise. 77 */ matchesLocalAddress(InetAddress address, int startPort, int endPort)78 boolean matchesLocalAddress(InetAddress address, int startPort, int endPort); 79 80 /** 81 * Filter using the remote address. 82 * 83 * @param address The local address. 84 * @param startPort Starting port. 85 * @param endPort Ending port. 86 * @return {@code true} if matches, {@code false} otherwise. 87 */ matchesRemoteAddress(InetAddress address, int startPort, int endPort)88 boolean matchesRemoteAddress(InetAddress address, int startPort, int endPort); 89 } 90 91 /** 92 * Constructor 93 * 94 * @param networkAgent The network agent to send events to. 95 * @param phone The phone instance. 96 */ QosCallbackTracker(@onNull NotifyQosSessionInterface networkAgent, @NonNull Phone phone)97 public QosCallbackTracker(@NonNull NotifyQosSessionInterface networkAgent, 98 @NonNull Phone phone) { 99 mQosBearerSessions = new HashMap<>(); 100 mCallbacksToFilter = new HashMap<>(); 101 mNetworkAgent = networkAgent; 102 mPhoneId = phone.getPhoneId(); 103 mRcsStats = RcsStats.getInstance(); 104 mLogTag = "QOSCT" + "-" + ((NetworkAgent) mNetworkAgent).getNetwork().getNetId(); 105 106 if (phone.isUsingNewDataStack()) { 107 //TODO: Replace the NetworkAgent in the constructor with TelephonyNetworkAgent 108 // after mPhone.isUsingNewDataStack() check is removed. 109 ((TelephonyNetworkAgent) networkAgent).registerCallback( 110 new TelephonyNetworkAgent.TelephonyNetworkAgentCallback(this::post) { 111 @Override 112 public void onQosCallbackRegistered(int qosCallbackId, 113 @NonNull QosFilter filter) { 114 addFilter(qosCallbackId, 115 new QosCallbackTracker.IFilter() { 116 @Override 117 public boolean matchesLocalAddress( 118 @NonNull InetAddress address, int startPort, 119 int endPort) { 120 return filter.matchesLocalAddress(address, startPort, 121 endPort); 122 } 123 124 @Override 125 public boolean matchesRemoteAddress( 126 @NonNull InetAddress address, int startPort, 127 int endPort) { 128 return filter.matchesRemoteAddress(address, startPort, 129 endPort); 130 } 131 }); 132 } 133 134 @Override 135 public void onQosCallbackUnregistered(int qosCallbackId) { 136 137 } 138 }); 139 } 140 } 141 142 /** 143 * Add new filter that is to receive events. 144 * 145 * @param callbackId the associated callback id. 146 * @param filter provides the matching logic. 147 */ addFilter(final int callbackId, final IFilter filter)148 public void addFilter(final int callbackId, final IFilter filter) { 149 post(() -> { 150 log("addFilter: callbackId=" + callbackId); 151 // Called from mDcNetworkAgent 152 mCallbacksToFilter.put(callbackId, filter); 153 154 //On first change. Check all sessions and send. 155 for (final QosBearerSession session : mQosBearerSessions.values()) { 156 if (doFiltersMatch(session, filter)) { 157 sendSessionAvailable(callbackId, session, filter); 158 159 notifyMetricDedicatedBearerListenerAdded(callbackId, session); 160 } 161 } 162 }); 163 } 164 165 /** 166 * Remove the filter with the associated callback id. 167 * 168 * @param callbackId the qos callback id. 169 */ removeFilter(final int callbackId)170 public void removeFilter(final int callbackId) { 171 post(() -> { 172 log("removeFilter: callbackId=" + callbackId); 173 mCallbacksToFilter.remove(callbackId); 174 notifyMetricDedicatedBearerListenerRemoved(callbackId); 175 }); 176 } 177 178 /** 179 * Update the list of qos sessions and send out corresponding events 180 * 181 * @param sessions the new list of qos sessions 182 */ updateSessions(@onNull final List<QosBearerSession> sessions)183 public void updateSessions(@NonNull final List<QosBearerSession> sessions) { 184 post(() -> { 185 log("updateSessions: sessions size=" + sessions.size()); 186 187 int bearerState = DEDICATED_BEARER_EVENT_STATE_NONE; 188 189 final List<QosBearerSession> sessionsToAdd = new ArrayList<>(); 190 final Map<Integer, QosBearerSession> incomingSessions = new HashMap<>(); 191 final HashSet<Integer> sessionsReportedToMetric = new HashSet<>(); 192 for (final QosBearerSession incomingSession : sessions) { 193 int sessionId = incomingSession.getQosBearerSessionId(); 194 incomingSessions.put(sessionId, incomingSession); 195 196 final QosBearerSession existingSession = mQosBearerSessions.get(sessionId); 197 for (final int callbackId : mCallbacksToFilter.keySet()) { 198 final IFilter filter = mCallbacksToFilter.get(callbackId); 199 200 final boolean incomingSessionMatch = doFiltersMatch(incomingSession, filter); 201 final boolean existingSessionMatch = 202 existingSession != null && doFiltersMatch(existingSession, filter); 203 204 if (!existingSessionMatch && incomingSessionMatch) { 205 // The filter matches now and didn't match earlier 206 sendSessionAvailable(callbackId, incomingSession, filter); 207 208 bearerState = DEDICATED_BEARER_EVENT_STATE_ADDED; 209 } 210 211 if (existingSessionMatch && incomingSessionMatch) { 212 // The same sessions matches the same filter, but if the qos changed, 213 // the callback still needs to be notified 214 if (!incomingSession.getQos().equals(existingSession.getQos())) { 215 sendSessionAvailable(callbackId, incomingSession, filter); 216 bearerState = DEDICATED_BEARER_EVENT_STATE_MODIFIED; 217 } 218 } 219 220 // this QosBearerSession has registered QosCallbackId 221 if (!sessionsReportedToMetric.contains(sessionId) && incomingSessionMatch) { 222 // this session has listener 223 notifyMetricDedicatedBearerEvent(incomingSession, bearerState, true); 224 sessionsReportedToMetric.add(sessionId); 225 } 226 } 227 228 // this QosBearerSession does not have registered QosCallbackId 229 if (!sessionsReportedToMetric.contains(sessionId)) { 230 // no listener is registered to this session 231 bearerState = DEDICATED_BEARER_EVENT_STATE_ADDED; 232 notifyMetricDedicatedBearerEvent(incomingSession, bearerState, false); 233 sessionsReportedToMetric.add(sessionId); 234 } 235 sessionsToAdd.add(incomingSession); 236 } 237 238 final List<Integer> sessionsToRemove = new ArrayList<>(); 239 sessionsReportedToMetric.clear(); 240 bearerState = DEDICATED_BEARER_EVENT_STATE_DELETED; 241 // Find sessions that no longer exist 242 for (final QosBearerSession existingSession : mQosBearerSessions.values()) { 243 final int sessionId = existingSession.getQosBearerSessionId(); 244 if (!incomingSessions.containsKey(sessionId)) { 245 for (final int callbackId : mCallbacksToFilter.keySet()) { 246 final IFilter filter = mCallbacksToFilter.get(callbackId); 247 // The filter matches which means it was previously available, and now is 248 // lost 249 if (doFiltersMatch(existingSession, filter)) { 250 bearerState = DEDICATED_BEARER_EVENT_STATE_DELETED; 251 sendSessionLost(callbackId, existingSession); 252 notifyMetricDedicatedBearerEvent(existingSession, bearerState, true); 253 sessionsReportedToMetric.add(sessionId); 254 } 255 } 256 sessionsToRemove.add(sessionId); 257 if (!sessionsReportedToMetric.contains(sessionId)) { 258 notifyMetricDedicatedBearerEvent(existingSession, bearerState, false); 259 sessionsReportedToMetric.add(sessionId); 260 } 261 } 262 } 263 264 // Add in the new or existing sessions with updated information 265 for (final QosBearerSession sessionToAdd : sessionsToAdd) { 266 mQosBearerSessions.put(sessionToAdd.getQosBearerSessionId(), sessionToAdd); 267 } 268 269 // Remove any old sessions 270 for (final int sessionToRemove : sessionsToRemove) { 271 mQosBearerSessions.remove(sessionToRemove); 272 } 273 }); 274 } 275 doFiltersMatch(final @NonNull QosBearerSession qosBearerSession, final @NonNull IFilter filter)276 private boolean doFiltersMatch(final @NonNull QosBearerSession qosBearerSession, 277 final @NonNull IFilter filter) { 278 return getMatchingQosBearerFilter(qosBearerSession, filter) != null; 279 } 280 matchesByLocalAddress(final @NonNull QosBearerFilter sessionFilter, final @NonNull IFilter filter)281 private boolean matchesByLocalAddress(final @NonNull QosBearerFilter sessionFilter, 282 final @NonNull IFilter filter) { 283 if (sessionFilter.getLocalPortRange() == null) return false; 284 for (final LinkAddress qosAddress : sessionFilter.getLocalAddresses()) { 285 return filter.matchesLocalAddress(qosAddress.getAddress(), 286 sessionFilter.getLocalPortRange().getStart(), 287 sessionFilter.getLocalPortRange().getEnd()); 288 } 289 return false; 290 } 291 matchesByRemoteAddress(@onNull QosBearerFilter sessionFilter, final @NonNull IFilter filter)292 private boolean matchesByRemoteAddress(@NonNull QosBearerFilter sessionFilter, 293 final @NonNull IFilter filter) { 294 if (sessionFilter.getRemotePortRange() == null) return false; 295 for (final LinkAddress qosAddress : sessionFilter.getRemoteAddresses()) { 296 return filter.matchesRemoteAddress(qosAddress.getAddress(), 297 sessionFilter.getRemotePortRange().getStart(), 298 sessionFilter.getRemotePortRange().getEnd()); 299 } 300 return false; 301 } 302 matchesByRemoteAndLocalAddress(@onNull QosBearerFilter sessionFilter, final @NonNull IFilter filter)303 private boolean matchesByRemoteAndLocalAddress(@NonNull QosBearerFilter sessionFilter, 304 final @NonNull IFilter filter) { 305 if (sessionFilter.getLocalPortRange() == null 306 || sessionFilter.getRemotePortRange() == null) return false; 307 for (final LinkAddress remoteAddress : sessionFilter.getRemoteAddresses()) { 308 for (final LinkAddress localAddress : sessionFilter.getLocalAddresses()) { 309 return filter.matchesRemoteAddress(remoteAddress.getAddress(), 310 sessionFilter.getRemotePortRange().getStart(), 311 sessionFilter.getRemotePortRange().getEnd()) 312 && filter.matchesLocalAddress(localAddress.getAddress(), 313 sessionFilter.getLocalPortRange().getStart(), 314 sessionFilter.getLocalPortRange().getEnd()); 315 } 316 } 317 return false; 318 } 319 getFilterByPrecedence( @ullable QosBearerFilter qosFilter, QosBearerFilter sessionFilter)320 private QosBearerFilter getFilterByPrecedence( 321 @Nullable QosBearerFilter qosFilter, QosBearerFilter sessionFilter) { 322 // Find for the highest precedence filter, lower the value is the higher the precedence 323 return qosFilter == null || sessionFilter.getPrecedence() < qosFilter.getPrecedence() 324 ? sessionFilter : qosFilter; 325 } 326 getMatchingQosBearerFilter( @onNull QosBearerSession qosBearerSession, final @NonNull IFilter filter)327 private @Nullable QosBearerFilter getMatchingQosBearerFilter( 328 @NonNull QosBearerSession qosBearerSession, final @NonNull IFilter filter) { 329 QosBearerFilter qosFilter = null; 330 331 for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) { 332 if (!sessionFilter.getLocalAddresses().isEmpty() 333 && !sessionFilter.getRemoteAddresses().isEmpty() 334 && sessionFilter.getLocalPortRange() != null 335 && sessionFilter.getLocalPortRange().isValid() 336 && sessionFilter.getRemotePortRange() != null 337 && sessionFilter.getRemotePortRange().isValid()) { 338 if (matchesByRemoteAndLocalAddress(sessionFilter, filter)) { 339 qosFilter = getFilterByPrecedence(qosFilter, sessionFilter); 340 } 341 } else if (!sessionFilter.getRemoteAddresses().isEmpty() 342 && sessionFilter.getRemotePortRange() != null 343 && sessionFilter.getRemotePortRange().isValid()) { 344 if (matchesByRemoteAddress(sessionFilter, filter)) { 345 qosFilter = getFilterByPrecedence(qosFilter, sessionFilter); 346 } 347 } else if (!sessionFilter.getLocalAddresses().isEmpty() 348 && sessionFilter.getLocalPortRange() != null 349 && sessionFilter.getLocalPortRange().isValid()) { 350 if (matchesByLocalAddress(sessionFilter, filter)) { 351 qosFilter = getFilterByPrecedence(qosFilter, sessionFilter); 352 } 353 } 354 } 355 return qosFilter; 356 } 357 sendSessionAvailable(final int callbackId, final @NonNull QosBearerSession session, @NonNull IFilter filter)358 private void sendSessionAvailable(final int callbackId, final @NonNull QosBearerSession session, 359 @NonNull IFilter filter) { 360 QosBearerFilter qosBearerFilter = getMatchingQosBearerFilter(session, filter); 361 List<InetSocketAddress> remoteAddresses = new ArrayList<>(); 362 if (qosBearerFilter.getRemoteAddresses().size() > 0 363 && qosBearerFilter.getRemotePortRange() != null) { 364 remoteAddresses.add( 365 new InetSocketAddress(qosBearerFilter.getRemoteAddresses().get(0).getAddress(), 366 qosBearerFilter.getRemotePortRange().getStart())); 367 } 368 369 if (session.getQos() instanceof EpsQos) { 370 EpsQos qos = (EpsQos) session.getQos(); 371 EpsBearerQosSessionAttributes epsBearerAttr = 372 new EpsBearerQosSessionAttributes(qos.getQci(), 373 qos.getUplinkBandwidth().getMaxBitrateKbps(), 374 qos.getDownlinkBandwidth().getMaxBitrateKbps(), 375 qos.getDownlinkBandwidth().getGuaranteedBitrateKbps(), 376 qos.getUplinkBandwidth().getGuaranteedBitrateKbps(), 377 remoteAddresses); 378 mNetworkAgent.notifyQosSessionAvailable( 379 callbackId, session.getQosBearerSessionId(), epsBearerAttr); 380 } else { 381 NrQos qos = (NrQos) session.getQos(); 382 NrQosSessionAttributes nrQosAttr = 383 new NrQosSessionAttributes(qos.get5Qi(), qos.getQfi(), 384 qos.getUplinkBandwidth().getMaxBitrateKbps(), 385 qos.getDownlinkBandwidth().getMaxBitrateKbps(), 386 qos.getDownlinkBandwidth().getGuaranteedBitrateKbps(), 387 qos.getUplinkBandwidth().getGuaranteedBitrateKbps(), 388 qos.getAveragingWindow(), remoteAddresses); 389 mNetworkAgent.notifyQosSessionAvailable( 390 callbackId, session.getQosBearerSessionId(), nrQosAttr); 391 } 392 393 // added to notify to Metric for passing DedicatedBearerEstablished info 394 notifyMetricDedicatedBearerListenerBearerUpdateSession(callbackId, session); 395 396 log("sendSessionAvailable, callbackId=" + callbackId); 397 } 398 sendSessionLost(int callbackId, @NonNull QosBearerSession session)399 private void sendSessionLost(int callbackId, @NonNull QosBearerSession session) { 400 mNetworkAgent.notifyQosSessionLost(callbackId, session.getQosBearerSessionId(), 401 session.getQos() instanceof EpsQos 402 ? QosSession.TYPE_EPS_BEARER : QosSession.TYPE_NR_BEARER); 403 log("sendSessionLost, callbackId=" + callbackId); 404 } 405 notifyMetricDedicatedBearerListenerAdded(final int callbackId, final @NonNull QosBearerSession session)406 private void notifyMetricDedicatedBearerListenerAdded(final int callbackId, 407 final @NonNull QosBearerSession session) { 408 409 final int slotId = mPhoneId; 410 final int rat = getRatInfoFromSessionInfo(session); 411 final int qci = getQCIFromSessionInfo(session); 412 413 mRcsStats.onImsDedicatedBearerListenerAdded(callbackId, slotId, rat, qci); 414 } 415 notifyMetricDedicatedBearerListenerBearerUpdateSession( final int callbackId, final @NonNull QosBearerSession session)416 private void notifyMetricDedicatedBearerListenerBearerUpdateSession( 417 final int callbackId, final @NonNull QosBearerSession session) { 418 mRcsStats.onImsDedicatedBearerListenerUpdateSession(callbackId, mPhoneId, 419 getRatInfoFromSessionInfo(session), getQCIFromSessionInfo(session), true); 420 } 421 notifyMetricDedicatedBearerListenerRemoved(final int callbackId)422 private void notifyMetricDedicatedBearerListenerRemoved(final int callbackId) { 423 mRcsStats.onImsDedicatedBearerListenerRemoved(callbackId); 424 } 425 getQCIFromSessionInfo(final QosBearerSession session)426 private int getQCIFromSessionInfo(final QosBearerSession session) { 427 if (session.getQos() instanceof EpsQos) { 428 return ((EpsQos) session.getQos()).getQci(); 429 } else if (session.getQos() instanceof NrQos) { 430 return ((NrQos) session.getQos()).get5Qi(); 431 } 432 433 return 0; 434 } 435 getRatInfoFromSessionInfo(final QosBearerSession session)436 private int getRatInfoFromSessionInfo(final QosBearerSession session) { 437 if (session.getQos() instanceof EpsQos) { 438 return TelephonyManager.NETWORK_TYPE_LTE; 439 } else if (session.getQos() instanceof NrQos) { 440 return TelephonyManager.NETWORK_TYPE_NR; 441 } 442 443 return 0; 444 } 445 doesLocalConnectionInfoExist(final QosBearerSession qosBearerSession)446 private boolean doesLocalConnectionInfoExist(final QosBearerSession qosBearerSession) { 447 for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) { 448 if (!sessionFilter.getLocalAddresses().isEmpty() 449 && sessionFilter.getLocalPortRange() != null 450 && sessionFilter.getLocalPortRange().isValid()) { 451 return true; 452 } 453 } 454 return false; 455 } 456 doesRemoteConnectionInfoExist(final QosBearerSession qosBearerSession)457 private boolean doesRemoteConnectionInfoExist(final QosBearerSession qosBearerSession) { 458 for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) { 459 if (!sessionFilter.getRemoteAddresses().isEmpty() 460 && sessionFilter.getRemotePortRange() != null 461 && sessionFilter.getRemotePortRange().isValid()) { 462 return true; 463 } 464 } 465 return false; 466 } 467 notifyMetricDedicatedBearerEvent(final QosBearerSession session, final int bearerState, final boolean hasListener)468 private void notifyMetricDedicatedBearerEvent(final QosBearerSession session, 469 final int bearerState, final boolean hasListener) { 470 final int slotId = mPhoneId; 471 int ratAtEnd = getRatInfoFromSessionInfo(session); 472 int qci = getQCIFromSessionInfo(session); 473 boolean localConnectionInfoReceived = doesLocalConnectionInfoExist(session); 474 boolean remoteConnectionInfoReceived = doesRemoteConnectionInfoExist(session); 475 476 mRcsStats.onImsDedicatedBearerEvent(slotId, ratAtEnd, qci, bearerState, 477 localConnectionInfoReceived, remoteConnectionInfoReceived, hasListener); 478 } 479 480 /** 481 * Log debug messages. 482 * @param s debug messages 483 */ log(@onNull String s)484 private void log(@NonNull String s) { 485 Rlog.d(mLogTag, s); 486 } 487 } 488