• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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