HomeSP.java revision 07a3f9d97394b97965e6754271fb66c1d6bd1753
1package com.android.server.wifi.hotspot2.pps; 2 3import android.telephony.SubscriptionManager; 4import android.util.Log; 5 6import com.android.server.wifi.SIMAccessor; 7import com.android.server.wifi.anqp.ANQPElement; 8import com.android.server.wifi.anqp.CellularNetwork; 9import com.android.server.wifi.anqp.DomainNameElement; 10import com.android.server.wifi.anqp.NAIRealmData; 11import com.android.server.wifi.anqp.NAIRealmElement; 12import com.android.server.wifi.anqp.RoamingConsortiumElement; 13import com.android.server.wifi.anqp.ThreeGPPNetworkElement; 14import com.android.server.wifi.hotspot2.AuthMatch; 15import com.android.server.wifi.hotspot2.NetworkDetail; 16import com.android.server.wifi.hotspot2.PasspointMatch; 17import com.android.server.wifi.hotspot2.Utils; 18 19import java.util.ArrayList; 20import java.util.Collection; 21import java.util.Collections; 22import java.util.HashSet; 23import java.util.List; 24import java.util.Map; 25import java.util.Set; 26 27import static com.android.server.wifi.anqp.Constants.ANQPElementType; 28 29public class HomeSP { 30 private final Map<String, Long> mSSIDs; // SSID, HESSID, [0,N] 31 private final String mFQDN; 32 private final DomainMatcher mDomainMatcher; 33 private final Set<String> mOtherHomePartners; 34 private final HashSet<Long> mRoamingConsortiums; // [0,N] 35 private final Set<Long> mMatchAnyOIs; // [0,N] 36 private final List<Long> mMatchAllOIs; // [0,N] 37 38 private final Credential mCredential; 39 40 // Informational: 41 private final String mFriendlyName; // [1] 42 private final String mIconURL; // [0,1] 43 44 public HomeSP(Map<String, Long> ssidMap, 45 /*@NotNull*/ String fqdn, 46 /*@NotNull*/ HashSet<Long> roamingConsortiums, 47 /*@NotNull*/ Set<String> otherHomePartners, 48 /*@NotNull*/ Set<Long> matchAnyOIs, 49 /*@NotNull*/ List<Long> matchAllOIs, 50 String friendlyName, 51 String iconURL, 52 Credential credential) { 53 54 mSSIDs = ssidMap; 55 List<List<String>> otherPartners = new ArrayList<List<String>>(otherHomePartners.size()); 56 for (String otherPartner : otherHomePartners) { 57 otherPartners.add(Utils.splitDomain(otherPartner)); 58 } 59 mOtherHomePartners = otherHomePartners; 60 mFQDN = fqdn; 61 mDomainMatcher = new DomainMatcher(Utils.splitDomain(fqdn), otherPartners); 62 mRoamingConsortiums = roamingConsortiums; 63 mMatchAnyOIs = matchAnyOIs; 64 mMatchAllOIs = matchAllOIs; 65 mFriendlyName = friendlyName; 66 mIconURL = iconURL; 67 mCredential = credential; 68 } 69 70 public HomeSP getClone(String password) { 71 if (getCredential().hasDisregardPassword()) { 72 return new HomeSP(mSSIDs, 73 mFQDN, 74 mRoamingConsortiums, 75 mOtherHomePartners, 76 mMatchAnyOIs, 77 mMatchAllOIs, 78 mFriendlyName, 79 mIconURL, 80 new Credential(mCredential, password)); 81 } 82 else { 83 return this; 84 } 85 } 86 87 public PasspointMatch match(NetworkDetail networkDetail, 88 Map<ANQPElementType, ANQPElement> anqpElementMap, 89 SIMAccessor simAccessor) { 90 91 if (mSSIDs.containsKey(networkDetail.getSSID())) { 92 Long hessid = mSSIDs.get(networkDetail.getSSID()); 93 if (hessid == null || networkDetail.getHESSID() == hessid) { 94 Log.d(Utils.hs2LogTag(getClass()), "match SSID"); 95 return PasspointMatch.HomeProvider; 96 } 97 } 98 99 Set<Long> anOIs = new HashSet<Long>(); 100 101 if (networkDetail.getRoamingConsortiums() != null) { 102 for (long oi : networkDetail.getRoamingConsortiums()) { 103 anOIs.add(oi); 104 } 105 } 106 RoamingConsortiumElement rcElement = anqpElementMap != null ? 107 (RoamingConsortiumElement) anqpElementMap.get(ANQPElementType.ANQPRoamingConsortium) 108 : null; 109 if (rcElement != null) { 110 anOIs.addAll(rcElement.getOIs()); 111 } 112 113 boolean authPossible = false; 114 115 if (!mMatchAllOIs.isEmpty()) { 116 boolean matchesAll = true; 117 118 for (long spOI : mMatchAllOIs) { 119 if (!anOIs.contains(spOI)) { 120 matchesAll = false; 121 break; 122 } 123 } 124 if (matchesAll) { 125 authPossible = true; 126 } 127 else { 128 if (anqpElementMap != null || networkDetail.getAnqpOICount() == 0) { 129 return PasspointMatch.Declined; 130 } 131 else { 132 return PasspointMatch.Incomplete; 133 } 134 } 135 } 136 137 if (!authPossible && 138 (!Collections.disjoint(mMatchAnyOIs, anOIs) || 139 !Collections.disjoint(mRoamingConsortiums, anOIs))) { 140 authPossible = true; 141 } 142 143 if (anqpElementMap == null) { 144 return PasspointMatch.Incomplete; 145 } 146 147 DomainNameElement domainNameElement = 148 (DomainNameElement) anqpElementMap.get(ANQPElementType.ANQPDomName); 149 NAIRealmElement naiRealmElement = 150 (NAIRealmElement) anqpElementMap.get(ANQPElementType.ANQPNAIRealm); 151 ThreeGPPNetworkElement threeGPPNetworkElement = 152 (ThreeGPPNetworkElement) anqpElementMap.get(ANQPElementType.ANQP3GPPNetwork); 153 154 if (domainNameElement != null) { 155 for (String domain : domainNameElement.getDomains()) { 156 List<String> anLabels = Utils.splitDomain(domain); 157 DomainMatcher.Match match = mDomainMatcher.isSubDomain(anLabels); 158 if (match != DomainMatcher.Match.None) { 159 return PasspointMatch.HomeProvider; 160 } 161 162 String imsi = simAccessor.getMatchingImsi(Utils.getMccMnc(anLabels)); 163 if (imsi != null) { 164 Log.d(Utils.hs2LogTag(getClass()), "Domain " + domain + 165 " matches IMSI " + imsi); 166 return PasspointMatch.HomeProvider; 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