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 @NonNull 57 private final String mLogTag; 58 @NonNull 59 private final TelephonyNetworkAgent mNetworkAgent; 60 @NonNull 61 private final Map<Integer, QosBearerSession> mQosBearerSessions; 62 @NonNull 63 private final RcsStats mRcsStats; 64 65 // We perform an exact match on the address 66 @NonNull 67 private final Map<Integer, IFilter> mCallbacksToFilter; 68 69 private final int mPhoneId; 70 71 /** 72 * QOS sessions filter interface 73 */ 74 public interface IFilter { 75 /** 76 * Filter using the local address. 77 * 78 * @param address The local address. 79 * @param startPort Starting port. 80 * @param endPort Ending port. 81 * @return {@code true} if matches, {@code false} otherwise. 82 */ matchesLocalAddress(InetAddress address, int startPort, int endPort)83 boolean matchesLocalAddress(InetAddress address, int startPort, int endPort); 84 85 /** 86 * Filter using the remote address. 87 * 88 * @param address The remote address. 89 * @param startPort Starting port. 90 * @param endPort Ending port. 91 * @return {@code true} if matches, {@code false} otherwise. 92 */ matchesRemoteAddress(InetAddress address, int startPort, int endPort)93 boolean matchesRemoteAddress(InetAddress address, int startPort, int endPort); 94 95 /** 96 * Filter using the protocol 97 * 98 * @param protocol ID 99 * @return {@code true} if matches, {@code false} otherwise. 100 */ matchesProtocol(int protocol)101 boolean matchesProtocol(int protocol); 102 } 103 104 /** 105 * Constructor 106 * 107 * @param networkAgent The network agent to send events to. 108 * @param phone The phone instance. 109 */ QosCallbackTracker(@onNull TelephonyNetworkAgent networkAgent, @NonNull Phone phone)110 public QosCallbackTracker(@NonNull TelephonyNetworkAgent networkAgent, @NonNull Phone phone) { 111 mQosBearerSessions = new HashMap<>(); 112 mCallbacksToFilter = new HashMap<>(); 113 mNetworkAgent = networkAgent; 114 mPhoneId = phone.getPhoneId(); 115 mRcsStats = RcsStats.getInstance(); 116 mLogTag = "QOSCT" + "-" + ((NetworkAgent) mNetworkAgent).getNetwork().getNetId(); 117 118 networkAgent.registerCallback( 119 new TelephonyNetworkAgent.TelephonyNetworkAgentCallback(this::post) { 120 @Override 121 public void onQosCallbackRegistered(int qosCallbackId, 122 @NonNull QosFilter filter) { 123 addFilter(qosCallbackId, 124 new QosCallbackTracker.IFilter() { 125 @Override 126 public boolean matchesLocalAddress( 127 @NonNull InetAddress address, int startPort, 128 int endPort) { 129 return filter.matchesLocalAddress(address, startPort, 130 endPort); 131 } 132 133 @Override 134 public boolean matchesRemoteAddress( 135 @NonNull InetAddress address, int startPort, 136 int endPort) { 137 return filter.matchesRemoteAddress(address, startPort, 138 endPort); 139 } 140 141 @Override 142 public boolean matchesProtocol(int protocol) { 143 return filter.matchesProtocol(protocol); 144 } 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 sendSessionLost(callbackId, existingSession); 259 notifyMetricDedicatedBearerEvent(existingSession, bearerState, true); 260 sessionsReportedToMetric.add(sessionId); 261 } 262 } 263 sessionsToRemove.add(sessionId); 264 if (!sessionsReportedToMetric.contains(sessionId)) { 265 notifyMetricDedicatedBearerEvent(existingSession, bearerState, false); 266 sessionsReportedToMetric.add(sessionId); 267 } 268 } 269 } 270 271 // Add in the new or existing sessions with updated information 272 for (final QosBearerSession sessionToAdd : sessionsToAdd) { 273 mQosBearerSessions.put(sessionToAdd.getQosBearerSessionId(), sessionToAdd); 274 } 275 276 // Remove any old sessions 277 for (final int sessionToRemove : sessionsToRemove) { 278 mQosBearerSessions.remove(sessionToRemove); 279 } 280 }); 281 } 282 doFiltersMatch(@onNull final QosBearerSession qosBearerSession, @NonNull final IFilter filter)283 private boolean doFiltersMatch(@NonNull final QosBearerSession qosBearerSession, 284 @NonNull final IFilter filter) { 285 return getMatchingQosBearerFilter(qosBearerSession, filter) != null; 286 } 287 matchesByLocalAddress(@onNull final QosBearerFilter sessionFilter, @NonNull final IFilter filter)288 private boolean matchesByLocalAddress(@NonNull final QosBearerFilter sessionFilter, 289 @NonNull final IFilter filter) { 290 int portStart; 291 int portEnd; 292 if (sessionFilter.getLocalPortRange() == null) { 293 portStart = QosBearerFilter.QOS_MIN_PORT; 294 portEnd = QosBearerFilter.QOS_MAX_PORT; 295 } else if (sessionFilter.getLocalPortRange().isValid()) { 296 portStart = sessionFilter.getLocalPortRange().getStart(); 297 portEnd = sessionFilter.getLocalPortRange().getEnd(); 298 } else { 299 return false; 300 } 301 if (sessionFilter.getLocalAddresses().isEmpty()) { 302 InetAddress anyAddress; 303 try { 304 anyAddress = InetAddress.getByAddress(new byte[] {0, 0, 0, 0}); 305 } catch (UnknownHostException e) { 306 return false; 307 } 308 return filter.matchesLocalAddress(anyAddress, portStart, portEnd); 309 } else { 310 for (final LinkAddress qosAddress : sessionFilter.getLocalAddresses()) { 311 return filter.matchesLocalAddress(qosAddress.getAddress(), portStart, portEnd); 312 } 313 } 314 return false; 315 } 316 matchesByRemoteAddress(@onNull QosBearerFilter sessionFilter, @NonNull final IFilter filter)317 private boolean matchesByRemoteAddress(@NonNull QosBearerFilter sessionFilter, 318 @NonNull final IFilter filter) { 319 int portStart; 320 int portEnd; 321 boolean result = false; 322 if (sessionFilter.getRemotePortRange() == null) { 323 portStart = QosBearerFilter.QOS_MIN_PORT; 324 portEnd = QosBearerFilter.QOS_MAX_PORT; 325 } else if (sessionFilter.getRemotePortRange().isValid()) { 326 portStart = sessionFilter.getRemotePortRange().getStart(); 327 portEnd = sessionFilter.getRemotePortRange().getEnd(); 328 } else { 329 return false; 330 } 331 if (sessionFilter.getRemoteAddresses().isEmpty()) { 332 InetAddress anyAddress; 333 try { 334 anyAddress = InetAddress.getByAddress(new byte[] {0, 0, 0, 0}); 335 } catch (UnknownHostException e) { 336 return false; 337 } 338 result = filter.matchesRemoteAddress(anyAddress, portStart, portEnd); 339 } else { 340 for (final LinkAddress qosAddress : sessionFilter.getRemoteAddresses()) { 341 result = filter.matchesRemoteAddress(qosAddress.getAddress(), portStart, portEnd); 342 } 343 } 344 return result; 345 } 346 matchesByProtocol(@onNull QosBearerFilter sessionFilter, @NonNull final IFilter filter, boolean hasMatchedFilter)347 private boolean matchesByProtocol(@NonNull QosBearerFilter sessionFilter, 348 @NonNull final IFilter filter, boolean hasMatchedFilter) { 349 boolean result; 350 int protocol = sessionFilter.getProtocol(); 351 if (protocol == QosBearerFilter.QOS_PROTOCOL_TCP 352 || protocol == QosBearerFilter.QOS_PROTOCOL_UDP) { 353 result = filter.matchesProtocol(protocol); 354 } else { 355 // FWK currently doesn't support filtering based on protocol ID ESP & AH. We will follow 356 // match results of other filters. 357 result = hasMatchedFilter; 358 } 359 return result; 360 } 361 getFilterByPrecedence( @ullable QosBearerFilter qosFilter, QosBearerFilter sessionFilter)362 private QosBearerFilter getFilterByPrecedence( 363 @Nullable QosBearerFilter qosFilter, QosBearerFilter sessionFilter) { 364 // Find for the highest precedence filter, lower the value is the higher the precedence 365 return qosFilter == null || sessionFilter.getPrecedence() < qosFilter.getPrecedence() 366 ? sessionFilter : qosFilter; 367 } 368 369 @Nullable getMatchingQosBearerFilter( @onNull QosBearerSession qosBearerSession, @NonNull final IFilter filter)370 private QosBearerFilter getMatchingQosBearerFilter( 371 @NonNull QosBearerSession qosBearerSession, @NonNull final 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, @NonNull final QosBearerSession session, @NonNull IFilter filter)409 private void sendSessionAvailable(final int callbackId, @NonNull final QosBearerSession session, 410 @NonNull IFilter filter) { 411 QosBearerFilter qosBearerFilter = getMatchingQosBearerFilter(session, filter); 412 List<InetSocketAddress> remoteAddresses = new ArrayList<>(); 413 if (qosBearerFilter != null && !qosBearerFilter.getRemoteAddresses().isEmpty() 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, @NonNull final QosBearerSession session)457 private void notifyMetricDedicatedBearerListenerAdded(final int callbackId, 458 @NonNull final QosBearerSession session) { 459 460 final int rat = getRatInfoFromSessionInfo(session); 461 final int qci = getQCIFromSessionInfo(session); 462 463 mRcsStats.onImsDedicatedBearerListenerAdded(callbackId, mPhoneId, rat, qci); 464 } 465 notifyMetricDedicatedBearerListenerBearerUpdateSession( final int callbackId, @NonNull final QosBearerSession session)466 private void notifyMetricDedicatedBearerListenerBearerUpdateSession( 467 final int callbackId, @NonNull final QosBearerSession session) { 468 mRcsStats.onImsDedicatedBearerListenerUpdateSession(callbackId, mPhoneId, 469 getRatInfoFromSessionInfo(session), getQCIFromSessionInfo(session), true); 470 } 471 notifyMetricDedicatedBearerListenerRemoved(final int callbackId)472 private void notifyMetricDedicatedBearerListenerRemoved(final int callbackId) { 473 mRcsStats.onImsDedicatedBearerListenerRemoved(callbackId); 474 } 475 getQCIFromSessionInfo(final QosBearerSession session)476 private int getQCIFromSessionInfo(final QosBearerSession session) { 477 if (session.getQos() instanceof EpsQos) { 478 return ((EpsQos) session.getQos()).getQci(); 479 } else if (session.getQos() instanceof NrQos) { 480 return ((NrQos) session.getQos()).get5Qi(); 481 } 482 483 return 0; 484 } 485 getRatInfoFromSessionInfo(final QosBearerSession session)486 private int getRatInfoFromSessionInfo(final QosBearerSession session) { 487 if (session.getQos() instanceof EpsQos) { 488 return TelephonyManager.NETWORK_TYPE_LTE; 489 } else if (session.getQos() instanceof NrQos) { 490 return TelephonyManager.NETWORK_TYPE_NR; 491 } 492 493 return 0; 494 } 495 doesLocalConnectionInfoExist(final QosBearerSession qosBearerSession)496 private boolean doesLocalConnectionInfoExist(final QosBearerSession qosBearerSession) { 497 for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) { 498 if (!sessionFilter.getLocalAddresses().isEmpty() 499 && sessionFilter.getLocalPortRange() != null 500 && sessionFilter.getLocalPortRange().isValid()) { 501 return true; 502 } 503 } 504 return false; 505 } 506 doesRemoteConnectionInfoExist(final QosBearerSession qosBearerSession)507 private boolean doesRemoteConnectionInfoExist(final QosBearerSession qosBearerSession) { 508 for (final QosBearerFilter sessionFilter : qosBearerSession.getQosBearerFilterList()) { 509 if (!sessionFilter.getRemoteAddresses().isEmpty() 510 && sessionFilter.getRemotePortRange() != null 511 && sessionFilter.getRemotePortRange().isValid()) { 512 return true; 513 } 514 } 515 return false; 516 } 517 notifyMetricDedicatedBearerEvent(final QosBearerSession session, final int bearerState, final boolean hasListener)518 private void notifyMetricDedicatedBearerEvent(final QosBearerSession session, 519 final int bearerState, final boolean hasListener) { 520 int ratAtEnd = getRatInfoFromSessionInfo(session); 521 int qci = getQCIFromSessionInfo(session); 522 boolean localConnectionInfoReceived = doesLocalConnectionInfoExist(session); 523 boolean remoteConnectionInfoReceived = doesRemoteConnectionInfoExist(session); 524 525 mRcsStats.onImsDedicatedBearerEvent(mPhoneId, ratAtEnd, qci, bearerState, 526 localConnectionInfoReceived, remoteConnectionInfoReceived, hasListener); 527 } 528 529 /** 530 * Log debug messages. 531 * @param s debug messages 532 */ log(@onNull String s)533 private void log(@NonNull String s) { 534 Rlog.d(mLogTag, s); 535 } 536 } 537