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