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