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