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