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