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