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