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