HomeSP.java revision 3baa1769b394a44351228f1b6950706d56fe61fa
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.DomainNameElement;
8import com.android.server.wifi.anqp.NAIRealmElement;
9import com.android.server.wifi.anqp.RoamingConsortiumElement;
10import com.android.server.wifi.anqp.ThreeGPPNetworkElement;
11import com.android.server.wifi.hotspot2.AuthMatch;
12import com.android.server.wifi.hotspot2.NetworkDetail;
13import com.android.server.wifi.hotspot2.PasspointMatch;
14import com.android.server.wifi.hotspot2.Utils;
15
16import java.util.ArrayList;
17import java.util.Collection;
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<String> mOtherHomePartners;
31    private final HashSet<Long> mRoamingConsortiums;    // [0,N]
32    private final Set<Long> mMatchAnyOIs;           // [0,N]
33    private final List<Long> mMatchAllOIs;          // [0,N]
34
35    private final Credential mCredential;
36
37    // Informational:
38    private final String mFriendlyName;             // [1]
39    private final String mIconURL;                  // [0,1]
40
41    public HomeSP(Map<String, Long> ssidMap,
42                   /*@NotNull*/ String fqdn,
43                   /*@NotNull*/ HashSet<Long> roamingConsortiums,
44                   /*@NotNull*/ Set<String> otherHomePartners,
45                   /*@NotNull*/ Set<Long> matchAnyOIs,
46                   /*@NotNull*/ List<Long> matchAllOIs,
47                   String friendlyName,
48                   String iconURL,
49                   Credential credential) {
50
51        mSSIDs = ssidMap;
52        List<List<String>> otherPartners = new ArrayList<List<String>>(otherHomePartners.size());
53        for (String otherPartner : otherHomePartners) {
54            otherPartners.add(Utils.splitDomain(otherPartner));
55        }
56        mOtherHomePartners = otherHomePartners;
57        mFQDN = fqdn;
58        mDomainMatcher = new DomainMatcher(Utils.splitDomain(fqdn), otherPartners);
59        mRoamingConsortiums = roamingConsortiums;
60        mMatchAnyOIs = matchAnyOIs;
61        mMatchAllOIs = matchAllOIs;
62        mFriendlyName = friendlyName;
63        mIconURL = iconURL;
64        mCredential = credential;
65    }
66
67    public HomeSP getClone(String password) {
68        if (getCredential().hasDisregardPassword()) {
69            return new HomeSP(mSSIDs,
70                    mFQDN,
71                    mRoamingConsortiums,
72                    mOtherHomePartners,
73                    mMatchAnyOIs,
74                    mMatchAllOIs,
75                    mFriendlyName,
76                    mIconURL,
77                    new Credential(mCredential, password));
78        }
79        else {
80            return this;
81        }
82    }
83
84    public PasspointMatch match(NetworkDetail networkDetail,
85                                Map<ANQPElementType, ANQPElement> anqpElementMap,
86                                SIMAccessor simAccessor) {
87
88        PasspointMatch spMatch = matchSP(networkDetail, anqpElementMap, simAccessor);
89
90        if (spMatch == PasspointMatch.HomeProvider || spMatch == PasspointMatch.RoamingProvider) {
91            NAIRealmElement naiRealmElement =
92                    (NAIRealmElement) anqpElementMap.get(ANQPElementType.ANQPNAIRealm);
93            ThreeGPPNetworkElement threeGPPNetworkElement =
94                    (ThreeGPPNetworkElement) anqpElementMap.get(ANQPElementType.ANQP3GPPNetwork);
95
96            AuthMatch authMatch = naiRealmElement.match(mCredential, threeGPPNetworkElement);
97            Log.d(Utils.hs2LogTag(getClass()), networkDetail.toKeyString() + " match on " + mFQDN +
98                    ": " + spMatch + ", auth " + authMatch);
99            return authMatch == AuthMatch.None ? PasspointMatch.None : spMatch;
100        }
101        else {
102            return spMatch;
103        }
104    }
105
106    public PasspointMatch matchSP(NetworkDetail networkDetail,
107                                Map<ANQPElementType, ANQPElement> anqpElementMap,
108                                SIMAccessor simAccessor) {
109
110        if (mSSIDs.containsKey(networkDetail.getSSID())) {
111            Long hessid = mSSIDs.get(networkDetail.getSSID());
112            if (hessid == null || networkDetail.getHESSID() == hessid) {
113                Log.d(Utils.hs2LogTag(getClass()), "match SSID");
114                return PasspointMatch.HomeProvider;
115            }
116        }
117
118        Set<Long> anOIs = new HashSet<>();
119
120        if (networkDetail.getRoamingConsortiums() != null) {
121            for (long oi : networkDetail.getRoamingConsortiums()) {
122                anOIs.add(oi);
123            }
124        }
125        RoamingConsortiumElement rcElement = anqpElementMap != null ?
126                (RoamingConsortiumElement) anqpElementMap.get(ANQPElementType.ANQPRoamingConsortium)
127                : null;
128        if (rcElement != null) {
129            anOIs.addAll(rcElement.getOIs());
130        }
131
132        // It may seem reasonable to check for home provider match prior to checking for roaming
133        // relationship, but it is possible to avoid an ANQP query if it turns out that the
134        // "match all" rule fails based only on beacon info only.
135        boolean roamingMatch = false;
136
137        if (!mMatchAllOIs.isEmpty()) {
138            boolean matchesAll = true;
139
140            for (long spOI : mMatchAllOIs) {
141                if (!anOIs.contains(spOI)) {
142                    matchesAll = false;
143                    break;
144                }
145            }
146            if (matchesAll) {
147                roamingMatch = true;
148            }
149            else {
150                if (anqpElementMap != null || networkDetail.getAnqpOICount() == 0) {
151                    return PasspointMatch.Declined;
152                }
153                else {
154                    return PasspointMatch.Incomplete;
155                }
156            }
157        }
158
159        if (!roamingMatch &&
160                (!Collections.disjoint(mMatchAnyOIs, anOIs) ||
161                        !Collections.disjoint(mRoamingConsortiums, anOIs))) {
162            roamingMatch = true;
163        }
164
165        if (anqpElementMap == null) {
166            return PasspointMatch.Incomplete;
167        }
168
169        DomainNameElement domainNameElement =
170                (DomainNameElement) anqpElementMap.get(ANQPElementType.ANQPDomName);
171
172        if (domainNameElement != null) {
173            for (String domain : domainNameElement.getDomains()) {
174                List<String> anLabels = Utils.splitDomain(domain);
175                DomainMatcher.Match match = mDomainMatcher.isSubDomain(anLabels);
176                if (match != DomainMatcher.Match.None) {
177                    return PasspointMatch.HomeProvider;
178                }
179
180                /* This is fundamentally wrong: We can't match the ANQP data to something unrelated
181                 * to this Home SP. Commented out until this has been clarified by the WFA.
182                String imsi = simAccessor.getMatchingImsi(Utils.getMccMnc(anLabels));
183                if (imsi != null) {
184                    Log.d(Utils.hs2LogTag(getClass()), "Domain " + domain +
185                            " matches IMSI " + imsi);
186                    return PasspointMatch.HomeProvider;
187                }
188                */
189            }
190        }
191
192        return roamingMatch ? PasspointMatch.RoamingProvider : PasspointMatch.None;
193    }
194
195    public String getFQDN() { return mFQDN; }
196    public String getFriendlyName() { return mFriendlyName; }
197    public HashSet<Long> getRoamingConsortiums() { return mRoamingConsortiums; }
198    public Credential getCredential() { return mCredential; }
199
200    public Map<String, Long> getSSIDs() {
201        return mSSIDs;
202    }
203
204    public Collection<String> getOtherHomePartners() {
205        return mOtherHomePartners;
206    }
207
208    public Set<Long> getMatchAnyOIs() {
209        return mMatchAnyOIs;
210    }
211
212    public List<Long> getMatchAllOIs() {
213        return mMatchAllOIs;
214    }
215
216    public String getIconURL() {
217        return mIconURL;
218    }
219
220    public boolean deepEquals(HomeSP other) {
221        return mFQDN.equals(other.mFQDN) &&
222                mSSIDs.equals(other.mSSIDs) &&
223                mOtherHomePartners.equals(other.mOtherHomePartners) &&
224                mRoamingConsortiums.equals(other.mRoamingConsortiums) &&
225                mMatchAnyOIs.equals(other.mMatchAnyOIs) &&
226                mMatchAllOIs.equals(other.mMatchAllOIs) &&
227                mFriendlyName.equals(other.mFriendlyName) &&
228                Utils.compare(mIconURL, other.mIconURL) == 0 &&
229                mCredential.equals(other.mCredential);
230    }
231
232    @Override
233    public boolean equals(Object thatObject) {
234        if (this == thatObject) {
235            return true;
236        } else if (thatObject == null || getClass() != thatObject.getClass()) {
237            return false;
238        }
239
240        HomeSP that = (HomeSP) thatObject;
241        return mFQDN.equals(that.mFQDN);
242    }
243
244    @Override
245    public int hashCode() {
246        return mFQDN.hashCode();
247    }
248
249    @Override
250    public String toString() {
251        return "HomeSP{" +
252                "mSSIDs=" + mSSIDs +
253                ", mFQDN='" + mFQDN + '\'' +
254                ", mDomainMatcher=" + mDomainMatcher +
255                ", mRoamingConsortiums={" + Utils.roamingConsortiumsToString(mRoamingConsortiums) +
256                '}' +
257                ", mMatchAnyOIs={" + Utils.roamingConsortiumsToString(mMatchAnyOIs) + '}' +
258                ", mMatchAllOIs={" + Utils.roamingConsortiumsToString(mMatchAllOIs) + '}' +
259                ", mCredential=" + mCredential +
260                ", mFriendlyName='" + mFriendlyName + '\'' +
261                ", mIconURL='" + mIconURL + '\'' +
262                '}';
263    }
264}
265