1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wifi.hotspot2;
18 
19 import android.text.TextUtils;
20 
21 import com.android.server.wifi.IMSIParameter;
22 import com.android.server.wifi.hotspot2.anqp.CellularNetwork;
23 import com.android.server.wifi.hotspot2.anqp.DomainNameElement;
24 import com.android.server.wifi.hotspot2.anqp.NAIRealmData;
25 import com.android.server.wifi.hotspot2.anqp.NAIRealmElement;
26 import com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement;
27 import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement;
28 
29 import java.util.List;
30 
31 /**
32  * Utility class for providing matching functions against ANQP elements.
33  */
34 public class ANQPMatcher {
35     /**
36      * Match the domain names in the ANQP element against the provider's FQDN and SIM credential.
37      * The Domain Name ANQP element might contain domains for 3GPP network (e.g.
38      * wlan.mnc*.mcc*.3gppnetwork.org), so we should match that against the provider's SIM
39      * credential if one is provided.
40      *
41      * @param element The Domain Name ANQP element
42      * @param fqdn The FQDN to compare against
43      * @param imsiParam The IMSI parameter of the provider (needed only for IMSI matching)
44      * @param simImsi The IMSI from the installed SIM cards that best matched provider's
45      *                    IMSI parameter (needed only for IMSI matching)
46      * @return true if a match is found
47      */
matchDomainName(DomainNameElement element, String fqdn, IMSIParameter imsiParam, String simImsi)48     public static boolean matchDomainName(DomainNameElement element, String fqdn,
49             IMSIParameter imsiParam, String simImsi) {
50         if (element == null) {
51             return false;
52         }
53 
54         for (String domain : element.getDomains()) {
55             if (DomainMatcher.arg2SubdomainOfArg1(fqdn, domain)) {
56                 return true;
57             }
58 
59             if (imsiParam == null || simImsi == null) {
60                 continue;
61             }
62 
63             // Try to retrieve the MCC-MNC string from the domain (for 3GPP network domain) and
64             // match against the provider's SIM credential.
65             if (matchMccMnc(Utils.getMccMnc(Utils.splitDomain(domain)), imsiParam, simImsi)) {
66                 return true;
67             }
68         }
69         return false;
70     }
71 
72     /**
73      * Match the roaming consortium OIs in the ANQP element against the roaming consortium OIs
74      * of a provider.
75      *
76      * @param element The Roaming Consortium ANQP element
77      * @param providerOIs The roaming consortium OIs of the provider
78      * @param matchAll Indicates if a match with all OIs must be done
79      * @return Matched OI value if a match is found, 0 otherwise. If matchAll is true, then the
80      * first matched OI will be returned if and only if all elements match. If the caller is
81      * interested in matching all, the return value of non-0 can be interpreted as a positive
82      * output and a 0 result can be interpreted as a negative result.
83      */
matchRoamingConsortium(RoamingConsortiumElement element, long[] providerOIs, boolean matchAll)84     public static long matchRoamingConsortium(RoamingConsortiumElement element,
85             long[] providerOIs, boolean matchAll) {
86         if (element == null) return 0;
87         if (providerOIs == null) return 0;
88 
89         long matchedRcoi = 0;
90         List<Long> rcOIs = element.getOIs();
91         for (long oi : providerOIs) {
92             if (rcOIs.contains(oi)) {
93                 if (!matchAll) {
94                     return oi;
95                 }
96                 if (matchedRcoi == 0) matchedRcoi = oi;
97             } else if (matchAll) {
98                 return 0;
99             }
100         }
101         return matchedRcoi;
102     }
103 
104     /**
105      * Match the NAI realm in the ANQP element against the realm and authentication method of
106      * a provider.
107      *
108      * @param element The NAI Realm ANQP element
109      * @param realm The realm of the provider's credential
110      * @return true if there is a NAI Realm match, false otherwise
111      */
matchNAIRealm(NAIRealmElement element, String realm)112     public static boolean matchNAIRealm(NAIRealmElement element, String realm) {
113         if (element == null || element.getRealmDataList().isEmpty()) {
114             return false;
115         }
116 
117         for (NAIRealmData realmData : element.getRealmDataList()) {
118             if (matchNAIRealmData(realmData, realm)) {
119                 return true;
120             }
121         }
122         return false;
123     }
124 
125     /**
126      * Match the 3GPP Network in the ANQP element against the SIM credential of a provider.
127      *
128      * @param element 3GPP Network ANQP element
129      * @param imsiParam The IMSI parameter of the provider's SIM credential
130      * @param simImsi The IMSI from the installed SIM cards that best matched provider's
131      *                    IMSI parameter
132      * @return true if a match is found
133      */
matchThreeGPPNetwork(ThreeGPPNetworkElement element, IMSIParameter imsiParam, String simImsi)134     public static  boolean matchThreeGPPNetwork(ThreeGPPNetworkElement element,
135             IMSIParameter imsiParam, String simImsi) {
136         if (element == null) {
137             return false;
138         }
139         for (CellularNetwork network : element.getNetworks()) {
140             if (matchCellularNetwork(network, imsiParam, simImsi)) {
141                 return true;
142             }
143         }
144         return false;
145     }
146 
147     /**
148      * Match the given NAI Realm data against the realm and authentication method of a provider.
149      *
150      * @param realmData The NAI Realm data
151      * @param realm The realm of the provider's credential
152      * @return true if a match is found
153      */
matchNAIRealmData(NAIRealmData realmData, String realm)154     private static boolean matchNAIRealmData(NAIRealmData realmData, String realm) {
155         // Check for realm domain name match.
156         for (String realmStr : realmData.getRealms()) {
157             if (DomainMatcher.arg2SubdomainOfArg1(realm, realmStr)) {
158                 return true;
159             }
160         }
161         return false;
162     }
163 
164     /**
165      * Match a cellular network information in the 3GPP Network ANQP element against the SIM
166      * credential of a provider.
167      *
168      * @param network The cellular network that contained list of PLMNs
169      * @param imsiParam IMSI parameter of the provider
170      * @param simImsi The IMSI from the installed SIM cards that best matched provider's
171      *                    IMSI parameter
172      * @return true if a match is found
173      */
matchCellularNetwork(CellularNetwork network, IMSIParameter imsiParam, String simImsi)174     private static boolean matchCellularNetwork(CellularNetwork network, IMSIParameter imsiParam,
175             String simImsi) {
176         for (String plmn : network.getPlmns()) {
177             if (matchMccMnc(plmn, imsiParam, simImsi)) {
178                 return true;
179             }
180         }
181 
182         return false;
183     }
184 
185     /**
186      * Match a MCC-MNC against the SIM credential of a provider.
187      *
188      * @param mccMnc The string containing MCC-MNC
189      * @param imsiParam The IMSI parameter of the provider
190      * @param simImsi The IMSI from the installed SIM cards that best matched provider's
191      *                    IMSI parameter
192      * @return true if a match is found
193      */
matchMccMnc(String mccMnc, IMSIParameter imsiParam, String simImsi)194     private static boolean matchMccMnc(String mccMnc, IMSIParameter imsiParam,
195             String simImsi) {
196         if (imsiParam == null || TextUtils.isEmpty(simImsi) || mccMnc == null) {
197             return false;
198         }
199         // Match against the IMSI parameter in the provider.
200         if (!imsiParam.matchesMccMnc(mccMnc)) {
201             return false;
202         }
203         // Additional check for verifying the match with IMSI from the SIM card, since the IMSI
204         // parameter might not contain the full 6-digit MCC MNC (e.g. IMSI parameter is an IMSI
205         // prefix that contained less than 6-digit of numbers "12345*").
206         return simImsi.startsWith(mccMnc);
207     }
208 }
209