HomeSP.java revision 05d2f4e6f26834a94b53187e6121379a16749088
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 PasspointMatch match(NetworkDetail networkDetail,
69                                Map<ANQPElementType, ANQPElement> anqpElementMap,
70                                List<String> imsis) {
71
72        if (mSSIDs.containsKey(networkDetail.getSSID())) {
73            Long hessid = mSSIDs.get(networkDetail.getSSID());
74            if (hessid == null || networkDetail.getHESSID() == hessid) {
75                Log.d("HS2J", "match SSID");
76                return PasspointMatch.HomeProvider;
77            }
78        }
79
80        Set<Long> anOIs = new HashSet<Long>();
81
82        if (networkDetail.getRoamingConsortiums() != null) {
83            for (long oi : networkDetail.getRoamingConsortiums()) {
84                anOIs.add(oi);
85            }
86        }
87        RoamingConsortiumElement rcElement = anqpElementMap != null ?
88                (RoamingConsortiumElement) anqpElementMap.get(ANQPElementType.ANQPRoamingConsortium)
89                : null;
90        if (rcElement != null) {
91            anOIs.addAll(rcElement.getOIs());
92        }
93
94        boolean authPossible = false;
95
96        if (!mMatchAllOIs.isEmpty()) {
97            boolean matchesAll = true;
98
99            for (long spOI : mMatchAllOIs) {
100                if (!anOIs.contains(spOI)) {
101                    matchesAll = false;
102                    break;
103                }
104            }
105            if (matchesAll) {
106                authPossible = true;
107            }
108            else {
109                if (anqpElementMap != null || networkDetail.getAnqpOICount() == 0) {
110                    return PasspointMatch.Declined;
111                }
112                else {
113                    return PasspointMatch.Incomplete;
114                }
115            }
116        }
117
118        if (!authPossible &&
119                (!Collections.disjoint(mMatchAnyOIs, anOIs) ||
120                        !Collections.disjoint(mRoamingConsortiums, anOIs))) {
121            authPossible = true;
122        }
123
124        if (anqpElementMap == null) {
125            return PasspointMatch.Incomplete;
126        }
127
128        DomainNameElement domainNameElement =
129                (DomainNameElement) anqpElementMap.get(ANQPElementType.ANQPDomName);
130        NAIRealmElement naiRealmElement =
131                (NAIRealmElement) anqpElementMap.get(ANQPElementType.ANQPNAIRealm);
132        ThreeGPPNetworkElement threeGPPNetworkElement =
133                (ThreeGPPNetworkElement) anqpElementMap.get(ANQPElementType.ANQP3GPPNetwork);
134
135        if (domainNameElement != null) {
136            for (String domain : domainNameElement.getDomains()) {
137                List<String> anLabels = Utils.splitDomain(domain);
138                DomainMatcher.Match match = mDomainMatcher.isSubDomain(anLabels);
139                if (match != DomainMatcher.Match.None) {
140                    return PasspointMatch.HomeProvider;
141                }
142
143                String mccMnc = Utils.getMccMnc(anLabels);
144                if (mccMnc != null) {
145                    for (String imsi : imsis) {
146                        if (imsi.startsWith(mccMnc)) {
147                            return PasspointMatch.HomeProvider;
148                        }
149                    }
150                }
151            }
152        }
153
154        if (!authPossible && naiRealmElement != null) {
155            AuthMatch authMatch = matchRealms(naiRealmElement, threeGPPNetworkElement);
156            if (authMatch != AuthMatch.None) {
157                return PasspointMatch.RoamingProvider;
158            }
159        }
160
161        return PasspointMatch.None;
162    }
163
164    private AuthMatch matchRealms(NAIRealmElement naiRealmElement,
165                                  ThreeGPPNetworkElement threeGPPNetworkElement) {
166        List<String> credRealm = Utils.splitDomain(mCredential.getRealm());
167
168        for (NAIRealmData naiRealmData : naiRealmElement.getRealmData()) {
169
170            DomainMatcher.Match match = DomainMatcher.Match.None;
171            for (String anRealm : naiRealmData.getRealms()) {
172                List<String> anRealmLabels = Utils.splitDomain(anRealm);
173                match = mDomainMatcher.isSubDomain(anRealmLabels);
174                if (match != DomainMatcher.Match.None) {
175                    break;
176                }
177                if (DomainMatcher.arg2SubdomainOfArg1(credRealm, anRealmLabels)) {
178                    match = DomainMatcher.Match.Secondary;
179                    break;
180                }
181            }
182
183            if (match != DomainMatcher.Match.None) {
184                if (mCredential.getImsi() != null) {
185                    // All the device has is one of EAP-SIM, AKA or AKA',
186                    // so a 3GPP element must appear and contain a matching MNC/MCC
187                    if (threeGPPNetworkElement == null) {
188                        return AuthMatch.None;
189                    }
190                    for (CellularNetwork network : threeGPPNetworkElement.getPlmns()) {
191                        if (network.matchIMSI(mCredential.getImsi())) {
192                            AuthMatch authMatch =
193                                    naiRealmData.matchEAPMethods(mCredential.getEAPMethod());
194                            if (authMatch != AuthMatch.None) {
195                                return authMatch;
196                            }
197                        }
198                    }
199                } else {
200                    AuthMatch authMatch = naiRealmData.matchEAPMethods(mCredential.getEAPMethod());
201                    if (authMatch != AuthMatch.None) {
202                        // Note: Something more intelligent could be done here based on the
203                        // authMatch value. It may be useful to have a secondary score to
204                        // distinguish more predictable EAP method/parameter matching.
205                        return authMatch;
206                    }
207                }
208            }
209        }
210        return AuthMatch.None;
211    }
212
213    public String getFQDN() { return mFQDN; }
214    public String getFriendlyName() { return mFriendlyName; }
215    public HashSet<Long> getRoamingConsortiums() { return mRoamingConsortiums; }
216    public Credential getCredential() { return mCredential; }
217
218    public Map<String, Long> getSSIDs() {
219        return mSSIDs;
220    }
221
222    public Collection<String> getOtherHomePartners() {
223        return mOtherHomePartners;
224    }
225
226    public Set<Long> getMatchAnyOIs() {
227        return mMatchAnyOIs;
228    }
229
230    public List<Long> getMatchAllOIs() {
231        return mMatchAllOIs;
232    }
233
234    public String getIconURL() {
235        return mIconURL;
236    }
237
238    public boolean deepEquals(HomeSP other) {
239        return mFQDN.equals(other.mFQDN) &&
240                mSSIDs.equals(other.mSSIDs) &&
241                mOtherHomePartners.equals(other.mOtherHomePartners) &&
242                mRoamingConsortiums.equals(other.mRoamingConsortiums) &&
243                mMatchAnyOIs.equals(other.mMatchAnyOIs) &&
244                mMatchAllOIs.equals(other.mMatchAllOIs) &&
245                mFriendlyName.equals(other.mFriendlyName) &&
246                Utils.compare(mIconURL, other.mIconURL) == 0 &&
247                mCredential.equals(other.mCredential);
248    }
249
250    @Override
251    public boolean equals(Object thatObject) {
252        if (this == thatObject) {
253            return true;
254        } else if (thatObject == null || getClass() != thatObject.getClass()) {
255            return false;
256        }
257
258        HomeSP that = (HomeSP) thatObject;
259        return mFQDN.equals(that.mFQDN);
260    }
261
262    @Override
263    public int hashCode() {
264        return mFQDN.hashCode();
265    }
266
267    @Override
268    public String toString() {
269        return "HomeSP{" +
270                "mSSIDs=" + mSSIDs +
271                ", mFQDN='" + mFQDN + '\'' +
272                ", mDomainMatcher=" + mDomainMatcher +
273                ", mRoamingConsortiums={" + Utils.roamingConsortiumsToString(mRoamingConsortiums) +
274                '}' +
275                ", mMatchAnyOIs={" + Utils.roamingConsortiumsToString(mMatchAnyOIs) + '}' +
276                ", mMatchAllOIs={" + Utils.roamingConsortiumsToString(mMatchAllOIs) + '}' +
277                ", mCredential=" + mCredential +
278                ", mFriendlyName='" + mFriendlyName + '\'' +
279                ", mIconURL='" + mIconURL + '\'' +
280                '}';
281    }
282}
283