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