PasspointMatchInfo.java revision ef1567e413c9ed5f5c4fdb9e354861632f7b2f87
1package com.android.server.wifi.hotspot2;
2
3import com.android.server.wifi.anqp.ANQPElement;
4import com.android.server.wifi.anqp.Constants;
5import com.android.server.wifi.anqp.HSConnectionCapabilityElement;
6import com.android.server.wifi.anqp.HSWanMetricsElement;
7import com.android.server.wifi.anqp.IPAddressTypeAvailabilityElement;
8import com.android.server.wifi.hotspot2.pps.HomeSP;
9
10import java.util.EnumMap;
11import java.util.HashMap;
12import java.util.Map;
13import static com.android.server.wifi.anqp.Constants.ANQPElementType;
14import static com.android.server.wifi.anqp.IPAddressTypeAvailabilityElement.IPv4Availability;
15import static com.android.server.wifi.anqp.IPAddressTypeAvailabilityElement.IPv6Availability;
16
17public class PasspointMatchInfo implements Comparable<PasspointMatchInfo> {
18    private final PasspointMatch mPasspointMatch;
19    private final NetworkDetail mNetworkDetail;
20    private final HomeSP mHomeSP;
21
22    private static final Map<NetworkDetail.Ant, Integer> sAntScores = new HashMap<>();
23
24    static {
25        sAntScores.put(NetworkDetail.Ant.FreePublic, 7);
26        sAntScores.put(NetworkDetail.Ant.ChargeablePublic, 6);
27        sAntScores.put(NetworkDetail.Ant.PrivateWithGuest, 5);
28        sAntScores.put(NetworkDetail.Ant.Private, 4);
29        sAntScores.put(NetworkDetail.Ant.Personal, 3);
30        sAntScores.put(NetworkDetail.Ant.EmergencyOnly, 2);
31        sAntScores.put(NetworkDetail.Ant.Wildcard, 1);
32        sAntScores.put(NetworkDetail.Ant.TestOrExperimental, 0);
33    }
34
35    public PasspointMatchInfo(PasspointMatch passpointMatch,
36                              NetworkDetail networkDetail, HomeSP homeSP) {
37        mPasspointMatch = passpointMatch;
38        mNetworkDetail = networkDetail;
39        mHomeSP = homeSP;
40    }
41
42    public PasspointMatch getPasspointMatch() {
43        return mPasspointMatch;
44    }
45
46    public NetworkDetail getNetworkDetail() {
47        return mNetworkDetail;
48    }
49
50    public HomeSP getHomeSP() {
51        return mHomeSP;
52    }
53
54    @Override
55    public int compareTo(PasspointMatchInfo that) {
56        if (getPasspointMatch() != that.getPasspointMatch()) {
57            return getPasspointMatch().compareTo(that.getPasspointMatch());
58        }
59
60        // IP Address Type Availability (802.11)
61
62        NetworkDetail n1 = getNetworkDetail();
63        NetworkDetail n2 = that.getNetworkDetail();
64
65        if (n1.isInternet() != n2.isInternet()) {
66            return n1.isInternet() ? 1 : -1;
67        }
68
69        if (n1.hasInterworking() && n2.hasInterworking() && n1.getAnt() != n2.getAnt()) {
70            int an1 = sAntScores.get(n1.getAnt());
71            int an2 = sAntScores.get(n2.getAnt());
72            return an1 - an2;
73        }
74
75        long score1 = n1.getCapacity() * n1.getChannelUtilization() * n1.getStationCount();
76        long score2 = n2.getCapacity() * n2.getChannelUtilization() * n2.getStationCount();
77
78        if (score1 != score2) {
79            return score1 < score2 ? 1 : -1;
80        }
81
82        int comp = subCompare(n1.getANQPElements(), n2.getANQPElements());
83        if (comp != 0) {
84            return comp;
85        }
86
87        return Utils.compare(n1.getHSRelease(), n2.getHSRelease());
88    }
89
90    private static final Map<IPv4Availability, Integer> sIP4Scores =
91            new EnumMap<>(IPv4Availability.class);
92    private static final Map<IPv6Availability, Integer> sIP6Scores =
93            new EnumMap<>(IPv6Availability.class);
94
95    private static final Map<Integer, Map<Integer, Integer>> sPortScores = new HashMap<>();
96
97    private static final int IPPROTO_ICMP = 1;
98    private static final int IPPROTO_TCP = 6;
99    private static final int IPPROTO_UDP = 17;
100    private static final int IPPROTO_ESP = 50;
101
102    static {
103        sIP4Scores.put(IPv4Availability.NotAvailable, 0);
104        sIP4Scores.put(IPv4Availability.PortRestricted, 1);
105        sIP4Scores.put(IPv4Availability.PortRestrictedAndSingleNAT, 1);
106        sIP4Scores.put(IPv4Availability.PortRestrictedAndDoubleNAT, 1);
107        sIP4Scores.put(IPv4Availability.Unknown, 1);
108        sIP4Scores.put(IPv4Availability.Public, 2);
109        sIP4Scores.put(IPv4Availability.SingleNAT, 2);
110        sIP4Scores.put(IPv4Availability.DoubleNAT, 2);
111
112        sIP6Scores.put(IPv6Availability.NotAvailable, 0);
113        sIP6Scores.put(IPv6Availability.Reserved, 1);
114        sIP6Scores.put(IPv6Availability.Unknown, 1);
115        sIP6Scores.put(IPv6Availability.Available, 2);
116
117        Map<Integer, Integer> tcpMap = new HashMap<>();
118        tcpMap.put(20, 1);
119        tcpMap.put(21, 1);
120        tcpMap.put(22, 3);
121        tcpMap.put(23, 2);
122        tcpMap.put(25, 8);
123        tcpMap.put(26, 8);
124        tcpMap.put(53, 3);
125        tcpMap.put(80, 10);
126        tcpMap.put(110, 6);
127        tcpMap.put(143, 6);
128        tcpMap.put(443, 10);
129        tcpMap.put(993, 6);
130        tcpMap.put(1723, 7);
131
132        Map<Integer, Integer> udpMap = new HashMap<>();
133        udpMap.put(53, 10);
134        udpMap.put(500, 7);
135        udpMap.put(5060, 10);
136        udpMap.put(4500, 4);
137
138        sPortScores.put(IPPROTO_TCP, tcpMap);
139        sPortScores.put(IPPROTO_UDP, udpMap);
140    }
141
142    private int subCompare(Map<Constants.ANQPElementType, ANQPElement> anqp1,
143                           Map<Constants.ANQPElementType, ANQPElement> anqp2) {
144        if (anqp1 == null || anqp2 == null) {
145            return 0;
146        }
147
148        int cmp;
149
150        HSWanMetricsElement w1 = (HSWanMetricsElement) anqp1.get(ANQPElementType.HSWANMetrics);
151        HSWanMetricsElement w2 = (HSWanMetricsElement) anqp2.get(ANQPElementType.HSWANMetrics);
152        if (w1 != null && w2 != null) {
153
154            boolean u1 = w1.getStatus() == HSWanMetricsElement.LinkStatus.Up;
155            boolean u2 = w2.getStatus() == HSWanMetricsElement.LinkStatus.Up;
156            cmp = Boolean.compare(u1, u2);
157            if (cmp != 0) {
158                return cmp;
159            }
160
161            cmp = Boolean.compare(w1.isCapped(), w2.isCapped());
162            if (cmp != 0) {
163                return cmp;
164            }
165
166            long bw1 = (w1.getDlSpeed() * (long)w1.getDlLoad()) * 8 +
167                       (w1.getUlSpeed() * (long)w1.getUlLoad()) * 2;
168            long bw2 = (w2.getDlSpeed() * (long)w2.getDlLoad()) * 8 +
169                       (w2.getUlSpeed() * (long)w2.getUlLoad()) * 2;
170            cmp = Long.compare(bw1, bw2);
171            if (cmp != 0) {
172                return cmp;
173            }
174        }
175
176        IPAddressTypeAvailabilityElement a1 =
177                (IPAddressTypeAvailabilityElement)anqp1.get(ANQPElementType.ANQPIPAddrAvailability);
178        IPAddressTypeAvailabilityElement a2 =
179                (IPAddressTypeAvailabilityElement)anqp2.get(ANQPElementType.ANQPIPAddrAvailability);
180        if (a1 != null && a2 != null) {
181            Integer as14 = sIP4Scores.get(a1.getV4Availability());
182            Integer as16 = sIP6Scores.get(a1.getV6Availability());
183            Integer as24 = sIP4Scores.get(a2.getV4Availability());
184            Integer as26 = sIP6Scores.get(a2.getV6Availability());
185            as14 = as14 != null ? as14 : 1;
186            as16 = as16 != null ? as16 : 1;
187            as24 = as24 != null ? as24 : 1;
188            as26 = as26 != null ? as26 : 1;
189            // Is IPv4 twice as important as IPv6???
190            int s1 = as14 * 2 + as16;
191            int s2 = as24 * 2 + as26;
192            cmp = Integer.compare(s1, s2);
193            if (cmp != 0) {
194                return cmp;
195            }
196        }
197
198        HSConnectionCapabilityElement cc1 =
199                (HSConnectionCapabilityElement) anqp1.get(ANQPElementType.HSConnCapability);
200        HSConnectionCapabilityElement cc2 =
201                (HSConnectionCapabilityElement) anqp2.get(ANQPElementType.HSConnCapability);
202
203        if (cc1 != null && cc2 != null) {
204            cmp = Integer.compare(protoScore(cc1), protoScore(cc2));
205            if (cmp != 0) {
206                return cmp;
207            }
208        }
209
210        return 0;
211    }
212
213    private static int protoScore(HSConnectionCapabilityElement cce) {
214        int score = 0;
215        for (HSConnectionCapabilityElement.ProtocolTuple tuple : cce.getStatusList()) {
216            int sign = tuple.getStatus() == HSConnectionCapabilityElement.ProtoStatus.Open ?
217                    1 : -1;
218
219            int elementScore = 1;
220            if (tuple.getProtocol() == IPPROTO_ICMP) {
221                elementScore = 1;
222            }
223            else if (tuple.getProtocol() == IPPROTO_ESP) {
224                elementScore = 5;
225            }
226            else {
227                Map<Integer, Integer> protoMap = sPortScores.get(tuple.getProtocol());
228                if (protoMap != null) {
229                    Integer portScore = protoMap.get(tuple.getPort());
230                    elementScore = portScore != null ? portScore : 0;
231                }
232            }
233            score += elementScore * sign;
234        }
235        return score;
236    }
237
238    @Override
239    public boolean equals(Object thatObject) {
240        if (this == thatObject) {
241            return true;
242        }
243        if (thatObject == null || getClass() != thatObject.getClass()) {
244            return false;
245        }
246
247        PasspointMatchInfo that = (PasspointMatchInfo)thatObject;
248
249        return getNetworkDetail().equals(that.getNetworkDetail()) &&
250                getHomeSP().equals(that.getHomeSP()) &&
251                getPasspointMatch().equals(that.getPasspointMatch());
252    }
253
254    @Override
255    public int hashCode() {
256        int result = mPasspointMatch != null ? mPasspointMatch.hashCode() : 0;
257        result = 31 * result + mNetworkDetail.hashCode();
258        result = 31 * result + (mHomeSP != null ? mHomeSP.hashCode() : 0);
259        return result;
260    }
261
262    @Override
263    public String toString() {
264        return "PasspointMatchInfo{" +
265                ", mPasspointMatch=" + mPasspointMatch +
266                ", mNetworkInfo=" + mNetworkDetail.getSSID() +
267                ", mHomeSP=" + mHomeSP.getFQDN() +
268                '}';
269    }
270}
271