| package com.android.server.wifi.hotspot2; |
| |
| import com.android.server.wifi.ScanDetail; |
| import com.android.server.wifi.anqp.ANQPElement; |
| import com.android.server.wifi.anqp.HSConnectionCapabilityElement; |
| import com.android.server.wifi.anqp.HSWanMetricsElement; |
| import com.android.server.wifi.anqp.IPAddressTypeAvailabilityElement; |
| import com.android.server.wifi.hotspot2.pps.HomeSP; |
| |
| import java.util.EnumMap; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import static com.android.server.wifi.anqp.Constants.ANQPElementType; |
| import static com.android.server.wifi.anqp.IPAddressTypeAvailabilityElement.IPv4Availability; |
| import static com.android.server.wifi.anqp.IPAddressTypeAvailabilityElement.IPv6Availability; |
| |
| public class PasspointMatchInfo implements Comparable<PasspointMatchInfo> { |
| private final PasspointMatch mPasspointMatch; |
| private final ScanDetail mScanDetail; |
| private final HomeSP mHomeSP; |
| private final int mScore; |
| |
| private static final Map<IPv4Availability, Integer> sIP4Scores = |
| new EnumMap<>(IPv4Availability.class); |
| private static final Map<IPv6Availability, Integer> sIP6Scores = |
| new EnumMap<>(IPv6Availability.class); |
| |
| private static final Map<Integer, Map<Integer, Integer>> sPortScores = new HashMap<>(); |
| |
| private static final int IPPROTO_ICMP = 1; |
| private static final int IPPROTO_TCP = 6; |
| private static final int IPPROTO_UDP = 17; |
| private static final int IPPROTO_ESP = 50; |
| private static final Map<NetworkDetail.Ant, Integer> sAntScores = new HashMap<>(); |
| |
| static { |
| // These are all arbitrarily chosen scores, subject to tuning. |
| |
| sAntScores.put(NetworkDetail.Ant.FreePublic, 4); |
| sAntScores.put(NetworkDetail.Ant.ChargeablePublic, 4); |
| sAntScores.put(NetworkDetail.Ant.PrivateWithGuest, 4); |
| sAntScores.put(NetworkDetail.Ant.Private, 4); |
| sAntScores.put(NetworkDetail.Ant.Personal, 2); |
| sAntScores.put(NetworkDetail.Ant.EmergencyOnly, 2); |
| sAntScores.put(NetworkDetail.Ant.Wildcard, 1); |
| sAntScores.put(NetworkDetail.Ant.TestOrExperimental, 0); |
| |
| sIP4Scores.put(IPv4Availability.NotAvailable, 0); |
| sIP4Scores.put(IPv4Availability.PortRestricted, 1); |
| sIP4Scores.put(IPv4Availability.PortRestrictedAndSingleNAT, 1); |
| sIP4Scores.put(IPv4Availability.PortRestrictedAndDoubleNAT, 1); |
| sIP4Scores.put(IPv4Availability.Unknown, 1); |
| sIP4Scores.put(IPv4Availability.Public, 2); |
| sIP4Scores.put(IPv4Availability.SingleNAT, 2); |
| sIP4Scores.put(IPv4Availability.DoubleNAT, 2); |
| |
| sIP6Scores.put(IPv6Availability.NotAvailable, 0); |
| sIP6Scores.put(IPv6Availability.Reserved, 1); |
| sIP6Scores.put(IPv6Availability.Unknown, 1); |
| sIP6Scores.put(IPv6Availability.Available, 2); |
| |
| Map<Integer, Integer> tcpMap = new HashMap<>(); |
| tcpMap.put(20, 1); |
| tcpMap.put(21, 1); |
| tcpMap.put(22, 3); |
| tcpMap.put(23, 2); |
| tcpMap.put(25, 8); |
| tcpMap.put(26, 8); |
| tcpMap.put(53, 3); |
| tcpMap.put(80, 10); |
| tcpMap.put(110, 6); |
| tcpMap.put(143, 6); |
| tcpMap.put(443, 10); |
| tcpMap.put(993, 6); |
| tcpMap.put(1723, 7); |
| |
| Map<Integer, Integer> udpMap = new HashMap<>(); |
| udpMap.put(53, 10); |
| udpMap.put(500, 7); |
| udpMap.put(5060, 10); |
| udpMap.put(4500, 4); |
| |
| sPortScores.put(IPPROTO_TCP, tcpMap); |
| sPortScores.put(IPPROTO_UDP, udpMap); |
| } |
| |
| |
| public PasspointMatchInfo(PasspointMatch passpointMatch, |
| ScanDetail scanDetail, HomeSP homeSP) { |
| mPasspointMatch = passpointMatch; |
| mScanDetail = scanDetail; |
| mHomeSP = homeSP; |
| |
| int score; |
| if (passpointMatch == PasspointMatch.HomeProvider) { |
| score = 100; |
| } |
| else if (passpointMatch == PasspointMatch.RoamingProvider) { |
| score = 0; |
| } |
| else { |
| score = -1000; // Don't expect to see anything not home or roaming. |
| } |
| |
| if (getNetworkDetail().getHSRelease() != null) { |
| score += getNetworkDetail().getHSRelease() != NetworkDetail.HSRelease.Unknown ? 50 : 0; |
| } |
| |
| if (getNetworkDetail().hasInterworking()) { |
| score += getNetworkDetail().isInternet() ? 20 : -20; |
| } |
| |
| score += (Math.max(200-getNetworkDetail().getStationCount(), 0) * |
| (255-getNetworkDetail().getChannelUtilization()) * |
| getNetworkDetail().getCapacity()) >>> 26; |
| // Gives a value of 23 max capped at 200 stations and max cap 31250 |
| |
| if (getNetworkDetail().hasInterworking()) { |
| score += sAntScores.get(getNetworkDetail().getAnt()); |
| } |
| |
| Map<ANQPElementType, ANQPElement> anqp = getNetworkDetail().getANQPElements(); |
| |
| if (anqp != null) { |
| HSWanMetricsElement wm = (HSWanMetricsElement) anqp.get(ANQPElementType.HSWANMetrics); |
| |
| if (wm != null) { |
| if (wm.getStatus() != HSWanMetricsElement.LinkStatus.Up || wm.isCapped()) { |
| score -= 1000; |
| } else { |
| long scaledSpeed = |
| wm.getDlSpeed() * (255 - wm.getDlLoad()) * 8 + |
| wm.getUlSpeed() * (255 - wm.getUlLoad()) * 2; |
| score += Math.min(scaledSpeed, 255000000L) >>> 23; |
| // Max value is 30 capped at 100Mb/s |
| } |
| } |
| |
| IPAddressTypeAvailabilityElement ipa = |
| (IPAddressTypeAvailabilityElement) anqp.get(ANQPElementType.ANQPIPAddrAvailability); |
| |
| if (ipa != null) { |
| Integer as14 = sIP4Scores.get(ipa.getV4Availability()); |
| Integer as16 = sIP6Scores.get(ipa.getV6Availability()); |
| as14 = as14 != null ? as14 : 1; |
| as16 = as16 != null ? as16 : 1; |
| // Is IPv4 twice as important as IPv6??? |
| score += as14 * 2 + as16; |
| } |
| |
| HSConnectionCapabilityElement cce = |
| (HSConnectionCapabilityElement) anqp.get(ANQPElementType.HSConnCapability); |
| |
| if (cce != null) { |
| score = Math.min(Math.max(protoScore(cce) >> 3, -10), 10); |
| } |
| } |
| |
| mScore = score; |
| } |
| |
| public PasspointMatch getPasspointMatch() { |
| return mPasspointMatch; |
| } |
| |
| public ScanDetail getScanDetail() { |
| return mScanDetail; |
| } |
| |
| public NetworkDetail getNetworkDetail() { |
| return mScanDetail.getNetworkDetail(); |
| } |
| |
| |
| public HomeSP getHomeSP() { |
| return mHomeSP; |
| } |
| |
| public int getScore() { |
| return mScore; |
| } |
| |
| @Override |
| public int compareTo(PasspointMatchInfo that) { |
| return getScore() - that.getScore(); |
| } |
| |
| private static int protoScore(HSConnectionCapabilityElement cce) { |
| int score = 0; |
| for (HSConnectionCapabilityElement.ProtocolTuple tuple : cce.getStatusList()) { |
| int sign = tuple.getStatus() == HSConnectionCapabilityElement.ProtoStatus.Open ? |
| 1 : -1; |
| |
| int elementScore = 1; |
| if (tuple.getProtocol() == IPPROTO_ICMP) { |
| elementScore = 1; |
| } |
| else if (tuple.getProtocol() == IPPROTO_ESP) { |
| elementScore = 5; |
| } |
| else { |
| Map<Integer, Integer> protoMap = sPortScores.get(tuple.getProtocol()); |
| if (protoMap != null) { |
| Integer portScore = protoMap.get(tuple.getPort()); |
| elementScore = portScore != null ? portScore : 0; |
| } |
| } |
| score += elementScore * sign; |
| } |
| return score; |
| } |
| |
| @Override |
| public boolean equals(Object thatObject) { |
| if (this == thatObject) { |
| return true; |
| } |
| if (thatObject == null || getClass() != thatObject.getClass()) { |
| return false; |
| } |
| |
| PasspointMatchInfo that = (PasspointMatchInfo)thatObject; |
| |
| return getNetworkDetail().equals(that.getNetworkDetail()) && |
| getHomeSP().equals(that.getHomeSP()) && |
| getPasspointMatch().equals(that.getPasspointMatch()); |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = mPasspointMatch != null ? mPasspointMatch.hashCode() : 0; |
| result = 31 * result + getNetworkDetail().hashCode(); |
| result = 31 * result + (mHomeSP != null ? mHomeSP.hashCode() : 0); |
| return result; |
| } |
| |
| @Override |
| public String toString() { |
| return "PasspointMatchInfo{" + |
| ", mPasspointMatch=" + mPasspointMatch + |
| ", mNetworkInfo=" + getNetworkDetail().getSSID() + |
| ", mHomeSP=" + mHomeSP.getFQDN() + |
| '}'; |
| } |
| } |