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