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