HomeSP.java revision a1edc185d46d85e04930a5e12b465de9fea64afe
1package com.android.server.wifi.hotspot2.pps; 2 3import android.util.Log; 4 5import com.android.server.wifi.anqp.ANQPElement; 6import com.android.server.wifi.anqp.CellularNetwork; 7import com.android.server.wifi.anqp.DomainNameElement; 8import com.android.server.wifi.anqp.NAIRealmData; 9import com.android.server.wifi.anqp.NAIRealmElement; 10import com.android.server.wifi.anqp.RoamingConsortiumElement; 11import com.android.server.wifi.anqp.ThreeGPPNetworkElement; 12import com.android.server.wifi.hotspot2.AuthMatch; 13import com.android.server.wifi.hotspot2.NetworkDetail; 14import com.android.server.wifi.hotspot2.PasspointMatch; 15import com.android.server.wifi.hotspot2.Utils; 16 17import java.util.ArrayList; 18import java.util.Collections; 19import java.util.HashSet; 20import java.util.List; 21import java.util.Map; 22import java.util.Set; 23 24import static com.android.server.wifi.anqp.Constants.ANQPElementType; 25 26public class HomeSP { 27 private final Map<String, Long> mSSIDs; // SSID, HESSID, [0,N] 28 private final String mFQDN; 29 private final DomainMatcher mDomainMatcher; 30 private final HashSet<Long> mRoamingConsortiums; // [0,N] 31 private final Set<Long> mMatchAnyOIs; // [0,N] 32 private final List<Long> mMatchAllOIs; // [0,N] 33 34 private final Credential mCredential; 35 36 // Informational: 37 private final String mFriendlyName; // [1] 38 private final String mIconURL; // [0,1] 39 40 public HomeSP(Map<String, Long> ssidMap, 41 /*@NotNull*/ String fqdn, 42 /*@NotNull*/ HashSet<Long> roamingConsortiums, 43 /*@NotNull*/ Set<String> otherHomePartners, 44 /*@NotNull*/ Set<Long> matchAnyOIs, 45 /*@NotNull*/ List<Long> matchAllOIs, 46 String friendlyName, 47 String iconURL, 48 Credential credential) { 49 50 mSSIDs = ssidMap; 51 List<List<String>> otherPartners = new ArrayList<List<String>>(otherHomePartners.size()); 52 for (String otherPartner : otherHomePartners) { 53 otherPartners.add(Utils.splitDomain(otherPartner)); 54 } 55 mFQDN = fqdn; 56 mDomainMatcher = new DomainMatcher(Utils.splitDomain(fqdn), otherPartners); 57 mRoamingConsortiums = roamingConsortiums; 58 mMatchAnyOIs = matchAnyOIs; 59 mMatchAllOIs = matchAllOIs; 60 mFriendlyName = friendlyName; 61 mIconURL = iconURL; 62 mCredential = credential; 63 } 64 65 public PasspointMatch match(NetworkDetail networkDetail, 66 Map<ANQPElementType, ANQPElement> anqpElementMap, 67 List<String> imsis) { 68 69 if (mSSIDs.containsKey(networkDetail.getSSID())) { 70 Long hessid = mSSIDs.get(networkDetail.getSSID()); 71 if (hessid == null || networkDetail.getHESSID() == hessid) { 72 Log.d("HS2J", "match SSID"); 73 return PasspointMatch.HomeProvider; 74 } 75 } 76 77 Set<Long> anOIs = new HashSet<Long>(); 78 79 if (networkDetail.getRoamingConsortiums() != null) { 80 for (long oi : networkDetail.getRoamingConsortiums()) { 81 anOIs.add(oi); 82 } 83 } 84 RoamingConsortiumElement rcElement = anqpElementMap != null ? 85 (RoamingConsortiumElement) anqpElementMap.get(ANQPElementType.ANQPRoamingConsortium) 86 : null; 87 if (rcElement != null) { 88 anOIs.addAll(rcElement.getOIs()); 89 } 90 91 boolean authPossible = false; 92 93 if (!mMatchAllOIs.isEmpty()) { 94 boolean matchesAll = true; 95 96 for (long spOI : mMatchAllOIs) { 97 if (!anOIs.contains(spOI)) { 98 matchesAll = false; 99 break; 100 } 101 } 102 if (matchesAll) { 103 authPossible = true; 104 } 105 else { 106 if (anqpElementMap != null || networkDetail.getAnqpOICount() == 0) { 107 return PasspointMatch.Declined; 108 } 109 else { 110 return PasspointMatch.Incomplete; 111 } 112 } 113 } 114 115 if (!authPossible && 116 (!Collections.disjoint(mMatchAnyOIs, anOIs) || 117 !Collections.disjoint(mRoamingConsortiums, anOIs))) { 118 authPossible = true; 119 } 120 121 if (anqpElementMap == null) { 122 return PasspointMatch.Incomplete; 123 } 124 125 DomainNameElement domainNameElement = 126 (DomainNameElement) anqpElementMap.get(ANQPElementType.ANQPDomName); 127 NAIRealmElement naiRealmElement = 128 (NAIRealmElement) anqpElementMap.get(ANQPElementType.ANQPNAIRealm); 129 ThreeGPPNetworkElement threeGPPNetworkElement = 130 (ThreeGPPNetworkElement) anqpElementMap.get(ANQPElementType.ANQP3GPPNetwork); 131 132 if (domainNameElement != null) { 133 for (String domain : domainNameElement.getDomains()) { 134 List<String> anLabels = Utils.splitDomain(domain); 135 DomainMatcher.Match match = mDomainMatcher.isSubDomain(anLabels); 136 if (match != DomainMatcher.Match.None) { 137 return PasspointMatch.HomeProvider; 138 } 139 140 String mccMnc = Utils.getMccMnc(anLabels); 141 if (mccMnc != null) { 142 for (String imsi : imsis) { 143 if (imsi.startsWith(mccMnc)) { 144 return PasspointMatch.HomeProvider; 145 } 146 } 147 } 148 } 149 } 150 151 if (!authPossible && naiRealmElement != null) { 152 AuthMatch authMatch = matchRealms(naiRealmElement, threeGPPNetworkElement); 153 if (authMatch != AuthMatch.None) { 154 return PasspointMatch.RoamingProvider; 155 } 156 } 157 158 return PasspointMatch.None; 159 } 160 161 private AuthMatch matchRealms(NAIRealmElement naiRealmElement, 162 ThreeGPPNetworkElement threeGPPNetworkElement) { 163 List<String> credRealm = Utils.splitDomain(mCredential.getRealm()); 164 165 for (NAIRealmData naiRealmData : naiRealmElement.getRealmData()) { 166 167 DomainMatcher.Match match = DomainMatcher.Match.None; 168 for (String anRealm : naiRealmData.getRealms()) { 169 List<String> anRealmLabels = Utils.splitDomain(anRealm); 170 match = mDomainMatcher.isSubDomain(anRealmLabels); 171 if (match != DomainMatcher.Match.None) { 172 break; 173 } 174 if (DomainMatcher.arg2SubdomainOfArg1(credRealm, anRealmLabels)) { 175 match = DomainMatcher.Match.Secondary; 176 break; 177 } 178 } 179 180 if (match != DomainMatcher.Match.None) { 181 if (mCredential.getImsi() != null) { 182 // All the device has is one of EAP-SIM, AKA or AKA', 183 // so a 3GPP element must appear and contain a matching MNC/MCC 184 if (threeGPPNetworkElement == null) { 185 return AuthMatch.None; 186 } 187 for (CellularNetwork network : threeGPPNetworkElement.getPlmns()) { 188 if (network.matchIMSI(mCredential.getImsi())) { 189 AuthMatch authMatch = 190 naiRealmData.matchEAPMethods(mCredential.getEAPMethod()); 191 if (authMatch != AuthMatch.None) { 192 return authMatch; 193 } 194 } 195 } 196 } else { 197 AuthMatch authMatch = naiRealmData.matchEAPMethods(mCredential.getEAPMethod()); 198 if (authMatch != AuthMatch.None) { 199 // Note: Something more intelligent could be done here based on the 200 // authMatch value. It may be useful to have a secondary score to 201 // distinguish more predictable EAP method/parameter matching. 202 return authMatch; 203 } 204 } 205 } 206 } 207 return AuthMatch.None; 208 } 209 210 public String getFQDN() { return mFQDN; } 211 public String getFriendlyName() { return mFriendlyName; } 212 public HashSet<Long> getRoamingConsortiums() { return mRoamingConsortiums; } 213 public Credential getCredential() { return mCredential; } 214 215 @Override 216 public boolean equals(Object thatObject) { 217 if (this == thatObject) { 218 return true; 219 } else if (thatObject == null || getClass() != thatObject.getClass()) { 220 return false; 221 } 222 223 HomeSP that = (HomeSP) thatObject; 224 return mFQDN.equals(that.mFQDN); 225 } 226 227 @Override 228 public int hashCode() { 229 return mFQDN.hashCode(); 230 } 231 232 @Override 233 public String toString() { 234 return "HomeSP{" + 235 "mSSIDs=" + mSSIDs + 236 ", mFQDN='" + mFQDN + '\'' + 237 ", mDomainMatcher=" + mDomainMatcher + 238 ", mRoamingConsortiums={" + Utils.roamingConsortiumsToString(mRoamingConsortiums) + 239 '}' + 240 ", mMatchAnyOIs={" + Utils.roamingConsortiumsToString(mMatchAnyOIs) + '}' + 241 ", mMatchAllOIs={" + Utils.roamingConsortiumsToString(mMatchAllOIs) + '}' + 242 ", mCredential=" + mCredential + 243 ", mFriendlyName='" + mFriendlyName + '\'' + 244 ", mIconURL='" + mIconURL + '\'' + 245 '}'; 246 } 247} 248