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