HomeSP.java revision a1edc185d46d85e04930a5e12b465de9fea64afe
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.Collections;
19import java.util.HashSet;
20import java.util.List;
21import java.util.Map;
22import java.util.Set;
23
24import static com.android.server.wifi.anqp.Constants.ANQPElementType;
25
26public class HomeSP {
27    private final Map<String, Long> mSSIDs;        // SSID, HESSID, [0,N]
28    private final String mFQDN;
29    private final DomainMatcher mDomainMatcher;
30    private final HashSet<Long> mRoamingConsortiums;    // [0,N]
31    private final Set<Long> mMatchAnyOIs;           // [0,N]
32    private final List<Long> mMatchAllOIs;          // [0,N]
33
34    private final Credential mCredential;
35
36    // Informational:
37    private final String mFriendlyName;             // [1]
38    private final String mIconURL;                  // [0,1]
39
40    public HomeSP(Map<String, Long> ssidMap,
41                   /*@NotNull*/ String fqdn,
42                   /*@NotNull*/ HashSet<Long> roamingConsortiums,
43                   /*@NotNull*/ Set<String> otherHomePartners,
44                   /*@NotNull*/ Set<Long> matchAnyOIs,
45                   /*@NotNull*/ List<Long> matchAllOIs,
46                   String friendlyName,
47                   String iconURL,
48                   Credential credential) {
49
50        mSSIDs = ssidMap;
51        List<List<String>> otherPartners = new ArrayList<List<String>>(otherHomePartners.size());
52        for (String otherPartner : otherHomePartners) {
53            otherPartners.add(Utils.splitDomain(otherPartner));
54        }
55        mFQDN = fqdn;
56        mDomainMatcher = new DomainMatcher(Utils.splitDomain(fqdn), otherPartners);
57        mRoamingConsortiums = roamingConsortiums;
58        mMatchAnyOIs = matchAnyOIs;
59        mMatchAllOIs = matchAllOIs;
60        mFriendlyName = friendlyName;
61        mIconURL = iconURL;
62        mCredential = credential;
63    }
64
65    public PasspointMatch match(NetworkDetail networkDetail,
66                                Map<ANQPElementType, ANQPElement> anqpElementMap,
67                                List<String> imsis) {
68
69        if (mSSIDs.containsKey(networkDetail.getSSID())) {
70            Long hessid = mSSIDs.get(networkDetail.getSSID());
71            if (hessid == null || networkDetail.getHESSID() == hessid) {
72                Log.d("HS2J", "match SSID");
73                return PasspointMatch.HomeProvider;
74            }
75        }
76
77        Set<Long> anOIs = new HashSet<Long>();
78
79        if (networkDetail.getRoamingConsortiums() != null) {
80            for (long oi : networkDetail.getRoamingConsortiums()) {
81                anOIs.add(oi);
82            }
83        }
84        RoamingConsortiumElement rcElement = anqpElementMap != null ?
85                (RoamingConsortiumElement) anqpElementMap.get(ANQPElementType.ANQPRoamingConsortium)
86                : null;
87        if (rcElement != null) {
88            anOIs.addAll(rcElement.getOIs());
89        }
90
91        boolean authPossible = false;
92
93        if (!mMatchAllOIs.isEmpty()) {
94            boolean matchesAll = true;
95
96            for (long spOI : mMatchAllOIs) {
97                if (!anOIs.contains(spOI)) {
98                    matchesAll = false;
99                    break;
100                }
101            }
102            if (matchesAll) {
103                authPossible = true;
104            }
105            else {
106                if (anqpElementMap != null || networkDetail.getAnqpOICount() == 0) {
107                    return PasspointMatch.Declined;
108                }
109                else {
110                    return PasspointMatch.Incomplete;
111                }
112            }
113        }
114
115        if (!authPossible &&
116                (!Collections.disjoint(mMatchAnyOIs, anOIs) ||
117                        !Collections.disjoint(mRoamingConsortiums, anOIs))) {
118            authPossible = true;
119        }
120
121        if (anqpElementMap == null) {
122            return PasspointMatch.Incomplete;
123        }
124
125        DomainNameElement domainNameElement =
126                (DomainNameElement) anqpElementMap.get(ANQPElementType.ANQPDomName);
127        NAIRealmElement naiRealmElement =
128                (NAIRealmElement) anqpElementMap.get(ANQPElementType.ANQPNAIRealm);
129        ThreeGPPNetworkElement threeGPPNetworkElement =
130                (ThreeGPPNetworkElement) anqpElementMap.get(ANQPElementType.ANQP3GPPNetwork);
131
132        if (domainNameElement != null) {
133            for (String domain : domainNameElement.getDomains()) {
134                List<String> anLabels = Utils.splitDomain(domain);
135                DomainMatcher.Match match = mDomainMatcher.isSubDomain(anLabels);
136                if (match != DomainMatcher.Match.None) {
137                    return PasspointMatch.HomeProvider;
138                }
139
140                String mccMnc = Utils.getMccMnc(anLabels);
141                if (mccMnc != null) {
142                    for (String imsi : imsis) {
143                        if (imsi.startsWith(mccMnc)) {
144                            return PasspointMatch.HomeProvider;
145                        }
146                    }
147                }
148            }
149        }
150
151        if (!authPossible && naiRealmElement != null) {
152            AuthMatch authMatch = matchRealms(naiRealmElement, threeGPPNetworkElement);
153            if (authMatch != AuthMatch.None) {
154                return PasspointMatch.RoamingProvider;
155            }
156        }
157
158        return PasspointMatch.None;
159    }
160
161    private AuthMatch matchRealms(NAIRealmElement naiRealmElement,
162                                  ThreeGPPNetworkElement threeGPPNetworkElement) {
163        List<String> credRealm = Utils.splitDomain(mCredential.getRealm());
164
165        for (NAIRealmData naiRealmData : naiRealmElement.getRealmData()) {
166
167            DomainMatcher.Match match = DomainMatcher.Match.None;
168            for (String anRealm : naiRealmData.getRealms()) {
169                List<String> anRealmLabels = Utils.splitDomain(anRealm);
170                match = mDomainMatcher.isSubDomain(anRealmLabels);
171                if (match != DomainMatcher.Match.None) {
172                    break;
173                }
174                if (DomainMatcher.arg2SubdomainOfArg1(credRealm, anRealmLabels)) {
175                    match = DomainMatcher.Match.Secondary;
176                    break;
177                }
178            }
179
180            if (match != DomainMatcher.Match.None) {
181                if (mCredential.getImsi() != null) {
182                    // All the device has is one of EAP-SIM, AKA or AKA',
183                    // so a 3GPP element must appear and contain a matching MNC/MCC
184                    if (threeGPPNetworkElement == null) {
185                        return AuthMatch.None;
186                    }
187                    for (CellularNetwork network : threeGPPNetworkElement.getPlmns()) {
188                        if (network.matchIMSI(mCredential.getImsi())) {
189                            AuthMatch authMatch =
190                                    naiRealmData.matchEAPMethods(mCredential.getEAPMethod());
191                            if (authMatch != AuthMatch.None) {
192                                return authMatch;
193                            }
194                        }
195                    }
196                } else {
197                    AuthMatch authMatch = naiRealmData.matchEAPMethods(mCredential.getEAPMethod());
198                    if (authMatch != AuthMatch.None) {
199                        // Note: Something more intelligent could be done here based on the
200                        // authMatch value. It may be useful to have a secondary score to
201                        // distinguish more predictable EAP method/parameter matching.
202                        return authMatch;
203                    }
204                }
205            }
206        }
207        return AuthMatch.None;
208    }
209
210    public String getFQDN() { return mFQDN; }
211    public String getFriendlyName() { return mFriendlyName; }
212    public HashSet<Long> getRoamingConsortiums() { return mRoamingConsortiums; }
213    public Credential getCredential() { return mCredential; }
214
215    @Override
216    public boolean equals(Object thatObject) {
217        if (this == thatObject) {
218            return true;
219        } else if (thatObject == null || getClass() != thatObject.getClass()) {
220            return false;
221        }
222
223        HomeSP that = (HomeSP) thatObject;
224        return mFQDN.equals(that.mFQDN);
225    }
226
227    @Override
228    public int hashCode() {
229        return mFQDN.hashCode();
230    }
231
232    @Override
233    public String toString() {
234        return "HomeSP{" +
235                "mSSIDs=" + mSSIDs +
236                ", mFQDN='" + mFQDN + '\'' +
237                ", mDomainMatcher=" + mDomainMatcher +
238                ", mRoamingConsortiums={" + Utils.roamingConsortiumsToString(mRoamingConsortiums) +
239                '}' +
240                ", mMatchAnyOIs={" + Utils.roamingConsortiumsToString(mMatchAnyOIs) + '}' +
241                ", mMatchAllOIs={" + Utils.roamingConsortiumsToString(mMatchAllOIs) + '}' +
242                ", mCredential=" + mCredential +
243                ", mFriendlyName='" + mFriendlyName + '\'' +
244                ", mIconURL='" + mIconURL + '\'' +
245                '}';
246    }
247}
248