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.net.UnknownHostException; 41 import java.util.ArrayList; 42 import java.util.HashMap; 43 import java.util.HashSet; 44 import java.util.List; 45 import java.util.Map; 46 47 /** 48 * Matches filters with qos sessions and send corresponding available and lost events. 49 */ 50 public class QosCallbackTracker extends Handler { 51 private static final int DEDICATED_BEARER_EVENT_STATE_NONE = 0; 52 private static final int DEDICATED_BEARER_EVENT_STATE_ADDED = 1; 53 private static final int DEDICATED_BEARER_EVENT_STATE_MODIFIED = 2; 54 private static final int DEDICATED_BEARER_EVENT_STATE_DELETED = 3; 55 56 private final @NonNull String mLogTag; 57 private final @NonNull TelephonyNetworkAgent 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 remote 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 * Filter using the protocol 92 * 93 * @param protocol ID 94 * @return {@code true} if matches, {@code false} otherwise. 95 */ matchesProtocol(int protocol)96 boolean matchesProtocol(int protocol); 97 } 98 99 /** 100 * Constructor 101 * 102 * @param networkAgent The network agent to send events to. 103 * @param phone The phone instance. 104 */ QosCallbackTracker(@onNull TelephonyNetworkAgent networkAgent, @NonNull Phone phone)105 public QosCallbackTracker(@NonNull TelephonyNetworkAgent networkAgent, @NonNull Phone phone) { 106 mQosBearerSessions = new HashMap<>(); 107 mCallbacksToFilter = new HashMap<>(); 108 mNetworkAgent = networkAgent; 109 mPhoneId = phone.getPhoneId(); 110 mRcsStats = RcsStats.getInstance(); 111 mLogTag = "QOSCT" + "-" + ((NetworkAgent) mNetworkAgent).getNetwork().getNetId(); 112 113 networkAgent.registerCallback( 114 new TelephonyNetworkAgent.TelephonyNetworkAgentCallback(this::post) { 115 @Override 116 public void onQosCallbackRegistered(int qosCallbackId, 117 @NonNull QosFilter filter) { 118 addFilter(qosCallbackId, 119 new QosCallbackTracker.IFilter() { 120 @Override 121 public boolean matchesLocalAddress( 122 @NonNull InetAddress address, int startPort, 123 int endPort) { 124 return filter.matchesLocalAddress(address, startPort, 125 endPort); 126 } 127 128 @Override 129 public boolean matchesRemoteAddress( 130 @NonNull InetAddress address, int startPort, 131 int endPort) { 132 return filter.matchesRemoteAddress(address, startPort, 133 endPort); 134 } 135 136 @Override 137 public boolean matchesProtocol(int protocol) { 138 return filter.matchesProtocol(protocol); 139 } 140 }); 141 } 142 143 @Override 144 public void onQosCallbackUnregistered(int qosCallbackId) { 145 146 } 147 }); 148 } 149 150 /** 151 * Add new filter that is to receive events. 152 * 153 * @param callbackId the associated callback id. 154 * @param filter provides the matching logic. 155 */ addFilter(final int callbackId, final IFilter filter)156 public void addFilter(final int callbackId, final IFilter filter) { 157 post(() -> { 158 log("addFilter: callbackId=" + callbackId); 159 // Called from mDcNetworkAgent 160 mCallbacksToFilter.put(callbackId, filter); 161 162 //On first change. Check all sessions and send. 163 for (final QosBearerSession session : mQosBearerSessions.values()) { 164 if (doFiltersMatch(session, filter)) { 165 sendSessionAvailable(callbackId, session, filter); 166 167 notifyMetricDedicatedBearerListenerAdded(callbackId, session); 168 } 169 } 170 }); 171 } 172 173 /** 174 * Remove the filter with the associated callback id. 175 * 176 * @param callbackId the qos callback id. 177 */ removeFilter(final int callbackId)178 public void removeFilter(final int callbackId) { 179 post(() -> { 180 log("removeFilter: callbackId=" + callbackId); 181 mCallbacksToFilter.remove(callbackId); 182 notifyMetricDedicatedBearerListenerRemoved(callbackId); 183 }); 184 } 185 186 /** 187 * Update the list of qos sessions and send out corresponding events 188 * 189 * @param sessions the new list of qos sessions 190 */ updateSessions(@onNull final List<QosBearerSession> sessions)191 public void updateSessions(@NonNull final List<QosBearerSession> sessions) { 192 post(() -> { 193 log("updateSessions: sessions size=" + sessions.size()); 194 195 int bearerState = DEDICATED_BEARER_EVENT_STATE_NONE; 196 197 final List<QosBearerSession> sessionsToAdd = new ArrayList<>(); 198 final Map<Integer, QosBearerSession> incomingSessions = new HashMap<>(); 199 final HashSet<Integer> sessionsReportedToMetric = new HashSet<>(); 200 for (final QosBearerSession incomingSession : sessions) { 201 int sessionId = incomingSession.getQosBearerSessionId(); 202 incomingSessions.put(sessionId, incomingSession); 203 204 final QosBearerSession existingSession = mQosBearerSessions.get(sessionId); 205 for (final int callbackId : mCallbacksToFilter.keySet()) { 206 final IFilter filter = mCallbacksToFilter.get(callbackId); 207 208 final boolean incomingSessionMatch = doFiltersMatch(incomingSession, filter); 209 final boolean existingSessionMatch = 210 existingSession != null && doFiltersMatch(existingSession, filter); 211 212 if (!existingSessionMatch && incomingSessionMatch) { 213 // The filter matches now and didn't match earlier 214 sendSessionAvailable(callbackId, incomingSession, filter); 215 216 bearerState = DEDICATED_BEARER_EVENT_STATE_ADDED; 217 } 218 219 if (existingSessionMatch && incomingSessionMatch) { 220 // The same sessions matches the same filter, but if the qos changed, 221 // the callback still needs to be notified 222 if (!incomingSession.getQos().equals(existingSession.getQos())) { 223 sendSessionAvailable(callbackId, incomingSession, filter); 224 bearerState = DEDICATED_BEARER_EVENT_STATE_MODIFIED; 225 } 226 } 227 228 // this QosBearerSession has registered QosCallbackId 229 if (!sessionsReportedToMetric.contains(sessionId) && incomingSessionMatch) { 230 // this session has listener 231 notifyMetricDedicatedBearerEvent(incomingSession, bearerState, true); 232 sessionsReportedToMetric.add(sessionId); 233 } 234 } 235 236 // this QosBearerSession does not have registered QosCallbackId 237 if (!sessionsReportedToMetric.contains(sessionId)) { 238 // no listener is registered to this session 239 bearerState = DEDICATED_BEARER_EVENT_STATE_ADDED; 240 notifyMetricDedicatedBearerEvent(incomingSession, bearerState, false); 241 sessionsReportedToMetric.add(sessionId); 242 } 243 sessionsToAdd.add(incomingSession); 244 } 245 246 final List<Integer> sessionsToRemove = new ArrayList<>(); 247 sessionsReportedToMetric.clear(); 248 bearerState = DEDICATED_BEARER_EVENT_STATE_DELETED; 249 // Find sessions that no longer exist 250 for (final QosBearerSession existingSession : mQosBearerSessions.values()) { 251 final int sessionId = existingSession.getQosBearerSessionId(); 252 if (!incomingSessions.containsKey(sessionId)) { 253 for (final int callbackId : mCallbacksToFilter.keySet()) { 254 final IFilter filter = mCallbacksToFilter.get(callbackId); 255 // The filter matches which means it was previously available, and now is 256 // lost 257 if (doFiltersMatch(existingSession, filter)) { 258 bearerState = DEDICATED_BEARER_EVENT_STATE_DELETED; 259 sendSessionLost(callbackId, existingSession); 260 notifyMetricDedicatedBearerEvent(existingSession, bearerState, true); 261 sessionsReportedToMetric.add(sessionId); 262 } 263 } 264 sessionsToRemove.add(sessionId); 265 if (!sessionsReportedToMetric.contains(sessionId)) { 266 notifyMetricDedicatedBearerEvent(existingSession, bearerState, false); 267 sessionsReportedToMetric.add(sessionId); 268 } 269 } 270 } 271 272 // Add in the new or existing sessions with updated information 273 for (final QosBearerSession sessionToAdd : sessionsToAdd) { 274 mQosBearerSessions.put(sessionToAdd.getQosBearerSessionId(), sessionToAdd); 275 } 276 277 // Remove any old sessions 278 for (final int sessionToRemove : sessionsToRemove) { 279 mQosBearerSessions.remove(sessionToRemove); 280 } 281 }); 282 } 283 doFiltersMatch(final @NonNull QosBearerSession qosBearerSession, final @NonNull IFilter filter)284 private boolean doFiltersMatch(final @NonNull QosBearerSession qosBearerSession, 285 final @NonNull IFilter filter) { 286 return getMatchingQosBearerFilter(qosBearerSession, filter) != null; 287 } 288 matchesByLocalAddress(final @NonNull QosBearerFilter sessionFilter, final @NonNull IFilter filter)289 private boolean matchesByLocalAddress(final @NonNull QosBearerFilter sessionFilter, 290 final @NonNull IFilter filter) { 291 int portStart; 292 int portEnd; 293 if (sessionFilter.getLocalPortRange() == null) { 294 portStart = QosBearerFilter.QOS_MIN_PORT; 295 portEnd = QosBearerFilter.QOS_MAX_PORT; 296 } else if (sessionFilter.getLocalPortRange().isValid()) { 297 portStart = sessionFilter.getLocalPortRange().getStart(); 298 portEnd = sessionFilter.getLocalPortRange().getEnd(); 299 } else { 300 return false; 301 } 302 if (sessionFilter.getLocalAddresses().isEmpty()) { 303 InetAddress anyAddress; 304 try { 305 anyAddress = InetAddress.getByAddress(new byte[] {0, 0, 0, 0}); 306 } catch (UnknownHostException e) { 307 return false; 308 } 309 return filter.matchesLocalAddress(anyAddress, portStart, portEnd); 310 } else { 311 for (final LinkAddress qosAddress : sessionFilter.getLocalAddresses()) { 312 return filter.matchesLocalAddress(qosAddress.getAddress(), portStart, portEnd); 313 } 314 } 315 return false; 316 } 317 matchesByRemoteAddress(@onNull QosBearerFilter sessionFilter, final @NonNull IFilter filter)318 private boolean matchesByRemoteAddress(@NonNull QosBearerFilter sessionFilter, 319 final @NonNull IFilter filter) { 320 int portStart; 321 int portEnd; 322 boolean result = false; 323 if (sessionFilter.getRemotePortRange() == null) { 324 portStart = QosBearerFilter.QOS_MIN_PORT; 325 portEnd = QosBearerFilter.QOS_MAX_PORT; 326 } else if (sessionFilter.getRemotePortRange().isValid()) { 327 portStart = sessionFilter.getRemotePortRange().getStart(); 328 portEnd = sessionFilter.getRemotePortRange().getEnd(); 329 } else { 330 return false; 331 } 332 if (sessionFilter.getRemoteAddresses().isEmpty()) { 333 InetAddress anyAddress; 334 try { 335 anyAddress = InetAddress.getByAddress(new byte[] {0, 0, 0, 0}); 336 } catch (UnknownHostException e) { 337 return false; 338 } 339 result = filter.matchesRemoteAddress(anyAddress, portStart, portEnd); 340 } else { 341 for (final LinkAddress qosAddress : sessionFilter.getRemoteAddresses()) { 342 result = filter.matchesRemoteAddress(qosAddress.getAddress(), portStart, portEnd); 343 } 344 } 345 return result; 346 } 347 matchesByProtocol(@onNull QosBearerFilter sessionFilter, final @NonNull IFilter filter, boolean hasMatchedFilter)348 private boolean matchesByProtocol(@NonNull QosBearerFilter sessionFilter, 349 final @NonNull IFilter filter, boolean hasMatchedFilter) { 350 boolean result = false; 351 int protocol = sessionFilter.getProtocol(); 352 if (protocol == QosBearerFilter.QOS_PROTOCOL_TCP 353 || protocol == QosBearerFilter.QOS_PROTOCOL_UDP) { 354 result = filter.matchesProtocol(protocol); 355 } else { 356 // FWK currently doesn't support filtering based on protocol ID ESP & AH. We will follow 357 // match results of other filters. 358 result = hasMatchedFilter; 359 } 360 return result; 361 } 362 getFilterByPrecedence( @ullable QosBearerFilter qosFilter, QosBearerFilter sessionFilter)363 private QosBearerFilter getFilterByPrecedence( 364 @Nullable QosBearerFilter qosFilter, QosBearerFilter sessionFilter) { 365 // Find for the highest precedence filter, lower the value is the higher the precedence 366 return qosFilter == null || sessionFilter.getPrecedence() < qosFilter.getPrecedence() 367 ? sessionFilter : qosFilter; 368 } 369 getMatchingQosBearerFilter( @onNull QosBearerSession qosBearerSession, final @NonNull IFilter filter)370 private @Nullable QosBearerFilter getMatchingQosBearerFilter( 371 @NonNull QosBearerSession qosBearerSession, final @NonNull IFilter filter) { 372 QosBearerFilter qosFilter = null; 373 374 for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) { 375 boolean unMatched = false; 376 boolean hasMatchedFilter = false; 377 if (!sessionFilter.getLocalAddresses().isEmpty() 378 || sessionFilter.getLocalPortRange() != null) { 379 if (!matchesByLocalAddress(sessionFilter, filter)) { 380 unMatched = true; 381 } else { 382 hasMatchedFilter = true; 383 } 384 } 385 if (!sessionFilter.getRemoteAddresses().isEmpty() 386 || sessionFilter.getRemotePortRange() != null) { 387 if (!matchesByRemoteAddress(sessionFilter, filter)) { 388 unMatched = true; 389 } else { 390 hasMatchedFilter = true; 391 } 392 } 393 394 if (sessionFilter.getProtocol() != QosBearerFilter.QOS_PROTOCOL_UNSPECIFIED) { 395 if (!matchesByProtocol(sessionFilter, filter, hasMatchedFilter)) { 396 unMatched = true; 397 } else { 398 hasMatchedFilter = true; 399 } 400 } 401 402 if (!unMatched && hasMatchedFilter) { 403 qosFilter = getFilterByPrecedence(qosFilter, sessionFilter); 404 } 405 } 406 return qosFilter; 407 } 408 sendSessionAvailable(final int callbackId, final @NonNull QosBearerSession session, @NonNull IFilter filter)409 private void sendSessionAvailable(final int callbackId, final @NonNull QosBearerSession session, 410 @NonNull IFilter filter) { 411 QosBearerFilter qosBearerFilter = getMatchingQosBearerFilter(session, filter); 412 List<InetSocketAddress> remoteAddresses = new ArrayList<>(); 413 if (qosBearerFilter.getRemoteAddresses().size() > 0 414 && qosBearerFilter.getRemotePortRange() != null) { 415 remoteAddresses.add( 416 new InetSocketAddress(qosBearerFilter.getRemoteAddresses().get(0).getAddress(), 417 qosBearerFilter.getRemotePortRange().getStart())); 418 } 419 420 if (session.getQos() instanceof EpsQos) { 421 EpsQos qos = (EpsQos) session.getQos(); 422 EpsBearerQosSessionAttributes epsBearerAttr = 423 new EpsBearerQosSessionAttributes(qos.getQci(), 424 qos.getUplinkBandwidth().getMaxBitrateKbps(), 425 qos.getDownlinkBandwidth().getMaxBitrateKbps(), 426 qos.getDownlinkBandwidth().getGuaranteedBitrateKbps(), 427 qos.getUplinkBandwidth().getGuaranteedBitrateKbps(), 428 remoteAddresses); 429 mNetworkAgent.sendQosSessionAvailable( 430 callbackId, session.getQosBearerSessionId(), epsBearerAttr); 431 } else { 432 NrQos qos = (NrQos) session.getQos(); 433 NrQosSessionAttributes nrQosAttr = 434 new NrQosSessionAttributes(qos.get5Qi(), qos.getQfi(), 435 qos.getUplinkBandwidth().getMaxBitrateKbps(), 436 qos.getDownlinkBandwidth().getMaxBitrateKbps(), 437 qos.getDownlinkBandwidth().getGuaranteedBitrateKbps(), 438 qos.getUplinkBandwidth().getGuaranteedBitrateKbps(), 439 qos.getAveragingWindow(), remoteAddresses); 440 mNetworkAgent.sendQosSessionAvailable( 441 callbackId, session.getQosBearerSessionId(), nrQosAttr); 442 } 443 444 // added to notify to Metric for passing DedicatedBearerEstablished info 445 notifyMetricDedicatedBearerListenerBearerUpdateSession(callbackId, session); 446 447 log("sendSessionAvailable, callbackId=" + callbackId); 448 } 449 sendSessionLost(int callbackId, @NonNull QosBearerSession session)450 private void sendSessionLost(int callbackId, @NonNull QosBearerSession session) { 451 mNetworkAgent.sendQosSessionLost(callbackId, session.getQosBearerSessionId(), 452 session.getQos() instanceof EpsQos 453 ? QosSession.TYPE_EPS_BEARER : QosSession.TYPE_NR_BEARER); 454 log("sendSessionLost, callbackId=" + callbackId); 455 } 456 notifyMetricDedicatedBearerListenerAdded(final int callbackId, final @NonNull QosBearerSession session)457 private void notifyMetricDedicatedBearerListenerAdded(final int callbackId, 458 final @NonNull QosBearerSession session) { 459 460 final int slotId = mPhoneId; 461 final int rat = getRatInfoFromSessionInfo(session); 462 final int qci = getQCIFromSessionInfo(session); 463 464 mRcsStats.onImsDedicatedBearerListenerAdded(callbackId, slotId, rat, qci); 465 } 466 notifyMetricDedicatedBearerListenerBearerUpdateSession( final int callbackId, final @NonNull QosBearerSession session)467 private void notifyMetricDedicatedBearerListenerBearerUpdateSession( 468 final int callbackId, final @NonNull QosBearerSession session) { 469 mRcsStats.onImsDedicatedBearerListenerUpdateSession(callbackId, mPhoneId, 470 getRatInfoFromSessionInfo(session), getQCIFromSessionInfo(session), true); 471 } 472 notifyMetricDedicatedBearerListenerRemoved(final int callbackId)473 private void notifyMetricDedicatedBearerListenerRemoved(final int callbackId) { 474 mRcsStats.onImsDedicatedBearerListenerRemoved(callbackId); 475 } 476 getQCIFromSessionInfo(final QosBearerSession session)477 private int getQCIFromSessionInfo(final QosBearerSession session) { 478 if (session.getQos() instanceof EpsQos) { 479 return ((EpsQos) session.getQos()).getQci(); 480 } else if (session.getQos() instanceof NrQos) { 481 return ((NrQos) session.getQos()).get5Qi(); 482 } 483 484 return 0; 485 } 486 getRatInfoFromSessionInfo(final QosBearerSession session)487 private int getRatInfoFromSessionInfo(final QosBearerSession session) { 488 if (session.getQos() instanceof EpsQos) { 489 return TelephonyManager.NETWORK_TYPE_LTE; 490 } else if (session.getQos() instanceof NrQos) { 491 return TelephonyManager.NETWORK_TYPE_NR; 492 } 493 494 return 0; 495 } 496 doesLocalConnectionInfoExist(final QosBearerSession qosBearerSession)497 private boolean doesLocalConnectionInfoExist(final QosBearerSession qosBearerSession) { 498 for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) { 499 if (!sessionFilter.getLocalAddresses().isEmpty() 500 && sessionFilter.getLocalPortRange() != null 501 && sessionFilter.getLocalPortRange().isValid()) { 502 return true; 503 } 504 } 505 return false; 506 } 507 doesRemoteConnectionInfoExist(final QosBearerSession qosBearerSession)508 private boolean doesRemoteConnectionInfoExist(final QosBearerSession qosBearerSession) { 509 for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) { 510 if (!sessionFilter.getRemoteAddresses().isEmpty() 511 && sessionFilter.getRemotePortRange() != null 512 && sessionFilter.getRemotePortRange().isValid()) { 513 return true; 514 } 515 } 516 return false; 517 } 518 notifyMetricDedicatedBearerEvent(final QosBearerSession session, final int bearerState, final boolean hasListener)519 private void notifyMetricDedicatedBearerEvent(final QosBearerSession session, 520 final int bearerState, final boolean hasListener) { 521 final int slotId = mPhoneId; 522 int ratAtEnd = getRatInfoFromSessionInfo(session); 523 int qci = getQCIFromSessionInfo(session); 524 boolean localConnectionInfoReceived = doesLocalConnectionInfoExist(session); 525 boolean remoteConnectionInfoReceived = doesRemoteConnectionInfoExist(session); 526 527 mRcsStats.onImsDedicatedBearerEvent(slotId, ratAtEnd, qci, bearerState, 528 localConnectionInfoReceived, remoteConnectionInfoReceived, hasListener); 529 } 530 531 /** 532 * Log debug messages. 533 * @param s debug messages 534 */ log(@onNull String s)535 private void log(@NonNull String s) { 536 Rlog.d(mLogTag, s); 537 } 538 } 539