• 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.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