PasspointMatchInfo.java revision 4d381bc39f5263effdae73ec99065eb299b806ca
1package com.android.server.wifi.hotspot2;
2
3import com.android.server.wifi.ScanDetail;
4import com.android.server.wifi.anqp.ANQPElement;
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;
13
14import static com.android.server.wifi.anqp.Constants.ANQPElementType;
15import static com.android.server.wifi.anqp.IPAddressTypeAvailabilityElement.IPv4Availability;
16import static com.android.server.wifi.anqp.IPAddressTypeAvailabilityElement.IPv6Availability;
17
18public class PasspointMatchInfo implements Comparable<PasspointMatchInfo> {
19    private final PasspointMatch mPasspointMatch;
20    private final ScanDetail mScanDetail;
21    private final HomeSP mHomeSP;
22    private final int mScore;
23
24    private static final Map<IPv4Availability, Integer> sIP4Scores =
25            new EnumMap<>(IPv4Availability.class);
26    private static final Map<IPv6Availability, Integer> sIP6Scores =
27            new EnumMap<>(IPv6Availability.class);
28
29    private static final Map<Integer, Map<Integer, Integer>> sPortScores = new HashMap<>();
30
31    private static final int IPPROTO_ICMP = 1;
32    private static final int IPPROTO_TCP = 6;
33    private static final int IPPROTO_UDP = 17;
34    private static final int IPPROTO_ESP = 50;
35    private static final Map<NetworkDetail.Ant, Integer> sAntScores = new HashMap<>();
36
37    static {
38        // These are all arbitrarily chosen scores, subject to tuning.
39
40        sAntScores.put(NetworkDetail.Ant.FreePublic, 4);
41        sAntScores.put(NetworkDetail.Ant.ChargeablePublic, 4);
42        sAntScores.put(NetworkDetail.Ant.PrivateWithGuest, 4);
43        sAntScores.put(NetworkDetail.Ant.Private, 4);
44        sAntScores.put(NetworkDetail.Ant.Personal, 2);
45        sAntScores.put(NetworkDetail.Ant.EmergencyOnly, 2);
46        sAntScores.put(NetworkDetail.Ant.Wildcard, 1);
47        sAntScores.put(NetworkDetail.Ant.TestOrExperimental, 0);
48
49        sIP4Scores.put(IPv4Availability.NotAvailable, 0);
50        sIP4Scores.put(IPv4Availability.PortRestricted, 1);
51        sIP4Scores.put(IPv4Availability.PortRestrictedAndSingleNAT, 1);
52        sIP4Scores.put(IPv4Availability.PortRestrictedAndDoubleNAT, 1);
53        sIP4Scores.put(IPv4Availability.Unknown, 1);
54        sIP4Scores.put(IPv4Availability.Public, 2);
55        sIP4Scores.put(IPv4Availability.SingleNAT, 2);
56        sIP4Scores.put(IPv4Availability.DoubleNAT, 2);
57
58        sIP6Scores.put(IPv6Availability.NotAvailable, 0);
59        sIP6Scores.put(IPv6Availability.Reserved, 1);
60        sIP6Scores.put(IPv6Availability.Unknown, 1);
61        sIP6Scores.put(IPv6Availability.Available, 2);
62
63        Map<Integer, Integer> tcpMap = new HashMap<>();
64        tcpMap.put(20, 1);
65        tcpMap.put(21, 1);
66        tcpMap.put(22, 3);
67        tcpMap.put(23, 2);
68        tcpMap.put(25, 8);
69        tcpMap.put(26, 8);
70        tcpMap.put(53, 3);
71        tcpMap.put(80, 10);
72        tcpMap.put(110, 6);
73        tcpMap.put(143, 6);
74        tcpMap.put(443, 10);
75        tcpMap.put(993, 6);
76        tcpMap.put(1723, 7);
77
78        Map<Integer, Integer> udpMap = new HashMap<>();
79        udpMap.put(53, 10);
80        udpMap.put(500, 7);
81        udpMap.put(5060, 10);
82        udpMap.put(4500, 4);
83
84        sPortScores.put(IPPROTO_TCP, tcpMap);
85        sPortScores.put(IPPROTO_UDP, udpMap);
86    }
87
88
89    public PasspointMatchInfo(PasspointMatch passpointMatch,
90                              ScanDetail scanDetail, HomeSP homeSP) {
91        mPasspointMatch = passpointMatch;
92        mScanDetail = scanDetail;
93        mHomeSP = homeSP;
94
95        int score;
96        if (passpointMatch == PasspointMatch.HomeProvider) {
97            score = 100;
98        }
99        else if (passpointMatch == PasspointMatch.RoamingProvider) {
100            score = 0;
101        }
102        else {
103            score = -1000;  // Don't expect to see anything not home or roaming.
104        }
105
106        if (getNetworkDetail().getHSRelease() != null) {
107            score += getNetworkDetail().getHSRelease() != NetworkDetail.HSRelease.Unknown ? 50 : 0;
108        }
109
110        if (getNetworkDetail().hasInterworking()) {
111            score += getNetworkDetail().isInternet() ? 20 : -20;
112        }
113
114        score += (Math.max(200-getNetworkDetail().getStationCount(), 0) *
115                (255-getNetworkDetail().getChannelUtilization()) *
116                getNetworkDetail().getCapacity()) >>> 26;
117                // Gives a value of 23 max capped at 200 stations and max cap 31250
118
119        if (getNetworkDetail().hasInterworking()) {
120            score += sAntScores.get(getNetworkDetail().getAnt());
121        }
122
123        Map<ANQPElementType, ANQPElement> anqp = getNetworkDetail().getANQPElements();
124
125        if (anqp != null) {
126            HSWanMetricsElement wm = (HSWanMetricsElement) anqp.get(ANQPElementType.HSWANMetrics);
127
128            if (wm != null) {
129                if (wm.getStatus() != HSWanMetricsElement.LinkStatus.Up || wm.isCapped()) {
130                    score -= 1000;
131                } else {
132                    long scaledSpeed =
133                            wm.getDlSpeed() * (255 - wm.getDlLoad()) * 8 +
134                                    wm.getUlSpeed() * (255 - wm.getUlLoad()) * 2;
135                    score += Math.min(scaledSpeed, 255000000L) >>> 23;
136                    // Max value is 30 capped at 100Mb/s
137                }
138            }
139
140            IPAddressTypeAvailabilityElement ipa =
141                    (IPAddressTypeAvailabilityElement) anqp.get(ANQPElementType.ANQPIPAddrAvailability);
142
143            if (ipa != null) {
144                Integer as14 = sIP4Scores.get(ipa.getV4Availability());
145                Integer as16 = sIP6Scores.get(ipa.getV6Availability());
146                as14 = as14 != null ? as14 : 1;
147                as16 = as16 != null ? as16 : 1;
148                // Is IPv4 twice as important as IPv6???
149                score += as14 * 2 + as16;
150            }
151
152            HSConnectionCapabilityElement cce =
153                    (HSConnectionCapabilityElement) anqp.get(ANQPElementType.HSConnCapability);
154
155            if (cce != null) {
156                score = Math.min(Math.max(protoScore(cce) >> 3, -10), 10);
157            }
158        }
159
160        mScore = score;
161    }
162
163    public PasspointMatch getPasspointMatch() {
164        return mPasspointMatch;
165    }
166
167    public ScanDetail getScanDetail() {
168        return mScanDetail;
169    }
170
171    public NetworkDetail getNetworkDetail() {
172        return mScanDetail.getNetworkDetail();
173    }
174
175
176    public HomeSP getHomeSP() {
177        return mHomeSP;
178    }
179
180    public int getScore() {
181        return mScore;
182    }
183
184    @Override
185    public int compareTo(PasspointMatchInfo that) {
186        return getScore() - that.getScore();
187    }
188
189    private static int protoScore(HSConnectionCapabilityElement cce) {
190        int score = 0;
191        for (HSConnectionCapabilityElement.ProtocolTuple tuple : cce.getStatusList()) {
192            int sign = tuple.getStatus() == HSConnectionCapabilityElement.ProtoStatus.Open ?
193                    1 : -1;
194
195            int elementScore = 1;
196            if (tuple.getProtocol() == IPPROTO_ICMP) {
197                elementScore = 1;
198            }
199            else if (tuple.getProtocol() == IPPROTO_ESP) {
200                elementScore = 5;
201            }
202            else {
203                Map<Integer, Integer> protoMap = sPortScores.get(tuple.getProtocol());
204                if (protoMap != null) {
205                    Integer portScore = protoMap.get(tuple.getPort());
206                    elementScore = portScore != null ? portScore : 0;
207                }
208            }
209            score += elementScore * sign;
210        }
211        return score;
212    }
213
214    @Override
215    public boolean equals(Object thatObject) {
216        if (this == thatObject) {
217            return true;
218        }
219        if (thatObject == null || getClass() != thatObject.getClass()) {
220            return false;
221        }
222
223        PasspointMatchInfo that = (PasspointMatchInfo)thatObject;
224
225        return getNetworkDetail().equals(that.getNetworkDetail()) &&
226                getHomeSP().equals(that.getHomeSP()) &&
227                getPasspointMatch().equals(that.getPasspointMatch());
228    }
229
230    @Override
231    public int hashCode() {
232        int result = mPasspointMatch != null ? mPasspointMatch.hashCode() : 0;
233        result = 31 * result + getNetworkDetail().hashCode();
234        result = 31 * result + (mHomeSP != null ? mHomeSP.hashCode() : 0);
235        return result;
236    }
237
238    @Override
239    public String toString() {
240        return "PasspointMatchInfo{" +
241                ", mPasspointMatch=" + mPasspointMatch +
242                ", mNetworkInfo=" + getNetworkDetail().getSSID() +
243                ", mHomeSP=" + mHomeSP.getFQDN() +
244                '}';
245    }
246}
247