1 /* 2 * Copyright 2020 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.google.android.iwlan.epdg; 18 19 import android.content.Context; 20 import android.net.DnsResolver; 21 import android.net.DnsResolver.DnsException; 22 import android.net.InetAddresses; 23 import android.net.Network; 24 import android.support.annotation.IntDef; 25 import android.support.annotation.NonNull; 26 import android.support.annotation.Nullable; 27 import android.telephony.CarrierConfigManager; 28 import android.telephony.CellIdentityGsm; 29 import android.telephony.CellIdentityLte; 30 import android.telephony.CellIdentityNr; 31 import android.telephony.CellIdentityWcdma; 32 import android.telephony.CellInfo; 33 import android.telephony.CellInfoGsm; 34 import android.telephony.CellInfoLte; 35 import android.telephony.CellInfoNr; 36 import android.telephony.CellInfoTdscdma; 37 import android.telephony.CellInfoWcdma; 38 import android.telephony.DataFailCause; 39 import android.telephony.SubscriptionInfo; 40 import android.telephony.SubscriptionManager; 41 import android.telephony.TelephonyManager; 42 import android.text.TextUtils; 43 import android.util.Log; 44 45 import com.android.internal.annotations.VisibleForTesting; 46 47 import com.google.android.iwlan.ErrorPolicyManager; 48 import com.google.android.iwlan.IwlanError; 49 import com.google.android.iwlan.IwlanHelper; 50 import com.google.android.iwlan.epdg.NaptrDnsResolver.NaptrTarget; 51 52 import java.net.Inet4Address; 53 import java.net.Inet6Address; 54 import java.net.InetAddress; 55 import java.net.UnknownHostException; 56 import java.util.*; 57 import java.util.concurrent.CompletableFuture; 58 import java.util.concurrent.ConcurrentHashMap; 59 import java.util.concurrent.ExecutionException; 60 import java.util.concurrent.TimeoutException; 61 import java.util.concurrent.TimeUnit; 62 63 public class EpdgSelector { 64 private static final String TAG = "EpdgSelector"; 65 private Context mContext; 66 private int mSlotId; 67 private static ConcurrentHashMap<Integer, EpdgSelector> mSelectorInstances = 68 new ConcurrentHashMap<>(); 69 private int mV4PcoId = -1; 70 private int mV6PcoId = -1; 71 private byte[] mV4PcoData = null; 72 private byte[] mV6PcoData = null; 73 @NonNull private ErrorPolicyManager mErrorPolicyManager; 74 75 private static final long DNS_RESOLVER_TIMEOUT_DURATION_SEC = 5L; 76 77 final Comparator<InetAddress> inetAddressComparator = 78 new Comparator<InetAddress>() { 79 @Override 80 public int compare(InetAddress ip1, InetAddress ip2) { 81 if ((ip1 instanceof Inet4Address) && (ip2 instanceof Inet6Address)) { 82 return -1; 83 } else if ((ip1 instanceof Inet6Address) && (ip2 instanceof Inet4Address)) { 84 return 1; 85 } else { 86 return 0; 87 } 88 } 89 }; 90 91 public static final int PROTO_FILTER_IPV4 = 0; 92 public static final int PROTO_FILTER_IPV6 = 1; 93 public static final int PROTO_FILTER_IPV4V6 = 2; 94 95 @IntDef({PROTO_FILTER_IPV4, PROTO_FILTER_IPV6, PROTO_FILTER_IPV4V6}) 96 @interface ProtoFilter {} 97 98 public interface EpdgSelectorCallback { 99 /*gives priority ordered list of addresses*/ onServerListChanged(int transactionId, ArrayList<InetAddress> validIPList)100 void onServerListChanged(int transactionId, ArrayList<InetAddress> validIPList); 101 onError(int transactionId, IwlanError error)102 void onError(int transactionId, IwlanError error); 103 } 104 105 @VisibleForTesting EpdgSelector(Context context, int slotId)106 EpdgSelector(Context context, int slotId) { 107 mContext = context; 108 mSlotId = slotId; 109 110 mErrorPolicyManager = ErrorPolicyManager.getInstance(mContext, mSlotId); 111 } 112 getSelectorInstance(Context context, int slotId)113 public static EpdgSelector getSelectorInstance(Context context, int slotId) { 114 mSelectorInstances.computeIfAbsent(slotId, k -> new EpdgSelector(context, slotId)); 115 return mSelectorInstances.get(slotId); 116 } 117 setPcoData(int pcoId, byte[] pcoData)118 public boolean setPcoData(int pcoId, byte[] pcoData) { 119 Log.d(TAG, "onReceive PcoId:" + String.format("0x%04x", pcoId) + " PcoData:" + pcoData); 120 121 int PCO_ID_IPV6 = 122 IwlanHelper.getConfig( 123 CarrierConfigManager.Iwlan.KEY_EPDG_PCO_ID_IPV6_INT, mContext, mSlotId); 124 int PCO_ID_IPV4 = 125 IwlanHelper.getConfig( 126 CarrierConfigManager.Iwlan.KEY_EPDG_PCO_ID_IPV4_INT, mContext, mSlotId); 127 128 Log.d( 129 TAG, 130 "PCO_ID_IPV6:" 131 + String.format("0x%04x", PCO_ID_IPV6) 132 + " PCO_ID_IPV4:" 133 + String.format("0x%04x", PCO_ID_IPV4)); 134 135 if (pcoId == PCO_ID_IPV4) { 136 mV4PcoId = pcoId; 137 mV4PcoData = pcoData; 138 return true; 139 } else if (pcoId == PCO_ID_IPV6) { 140 mV6PcoId = pcoId; 141 mV6PcoData = pcoData; 142 return true; 143 } 144 145 return false; 146 } 147 clearPcoData()148 public void clearPcoData() { 149 Log.d(TAG, "Clear PCO data"); 150 mV4PcoId = -1; 151 mV6PcoId = -1; 152 mV4PcoData = null; 153 mV6PcoData = null; 154 } 155 getIP( String domainName, int filter, List<InetAddress> validIpList, Network network)156 private Map.Entry<String, List<InetAddress>> getIP( 157 String domainName, int filter, List<InetAddress> validIpList, Network network) { 158 List<InetAddress> ipList = new ArrayList<InetAddress>(); 159 160 // Get All IP for each domain name 161 Log.d(TAG, "Input domainName : " + domainName); 162 163 if (InetAddresses.isNumericAddress(domainName)) { 164 try { 165 Log.d(TAG, domainName + " is a numeric ip address"); 166 ipList.add(InetAddress.getByName(domainName)); 167 } catch (UnknownHostException e) { 168 Log.e(TAG, "Exception when resolving domainName : " + domainName + ".", e); 169 } 170 } else { 171 try { 172 CompletableFuture<List<InetAddress>> result = new CompletableFuture(); 173 final DnsResolver.Callback<List<InetAddress>> cb = 174 new DnsResolver.Callback<List<InetAddress>>() { 175 @Override 176 public void onAnswer( 177 @NonNull final List<InetAddress> answer, final int rcode) { 178 if (rcode != 0) { 179 Log.e(TAG, "DnsResolver Response Code = " + rcode); 180 } 181 result.complete(answer); 182 } 183 184 @Override 185 public void onError(@Nullable final DnsResolver.DnsException error) { 186 Log.e(TAG, "Resolve DNS with error : " + error); 187 result.completeExceptionally(error); 188 } 189 }; 190 DnsResolver.getInstance() 191 .query(network, domainName, DnsResolver.FLAG_EMPTY, r -> r.run(), null, cb); 192 ipList = 193 new ArrayList<>( 194 result.get(DNS_RESOLVER_TIMEOUT_DURATION_SEC, TimeUnit.SECONDS)); 195 } catch (ExecutionException e) { 196 Log.e(TAG, "Cause of ExecutionException: ", e.getCause()); 197 } catch (InterruptedException e) { 198 if (Thread.currentThread().interrupted()) { 199 Thread.currentThread().interrupt(); 200 } 201 Log.e(TAG, "InterruptedException: ", e); 202 } catch (TimeoutException e) { 203 Log.e(TAG, "TimeoutException: ", e); 204 } 205 } 206 207 List<InetAddress> filteredIpList = new ArrayList<>(); 208 // Filter the IP list by input ProtoFilter 209 for (InetAddress ipAddress : ipList) { 210 switch (filter) { 211 case PROTO_FILTER_IPV4: 212 if (ipAddress instanceof Inet4Address) { 213 filteredIpList.add(ipAddress); 214 } 215 break; 216 case PROTO_FILTER_IPV6: 217 if (!IwlanHelper.isIpv4EmbeddedIpv6Address(ipAddress)) { 218 filteredIpList.add(ipAddress); 219 } 220 break; 221 case PROTO_FILTER_IPV4V6: 222 filteredIpList.add(ipAddress); 223 break; 224 default: 225 Log.d(TAG, "Invalid ProtoFilter : " + filter); 226 } 227 } 228 validIpList.addAll(filteredIpList); 229 230 return Map.entry(domainName, filteredIpList); 231 } 232 getPlmnList()233 private String[] getPlmnList() { 234 List<String> plmnsFromSubInfo = new ArrayList<>(); 235 236 List<String> plmnsFromCarrierConfig = 237 new ArrayList<>( 238 Arrays.asList( 239 IwlanHelper.getConfig( 240 CarrierConfigManager.Iwlan.KEY_MCC_MNCS_STRING_ARRAY, 241 mContext, 242 mSlotId))); 243 Log.d(TAG, "plmnsFromCarrierConfig:" + plmnsFromCarrierConfig); 244 245 // Get Ehplmns & mccmnc from SubscriptionManager 246 SubscriptionManager subscriptionManager = 247 mContext.getSystemService(SubscriptionManager.class); 248 if (subscriptionManager == null) { 249 Log.e(TAG, "SubscriptionManager is NULL"); 250 return plmnsFromCarrierConfig.toArray(new String[plmnsFromCarrierConfig.size()]); 251 } 252 253 SubscriptionInfo subInfo = 254 subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(mSlotId); 255 if (subInfo == null) { 256 Log.e(TAG, "SubscriptionInfo is NULL"); 257 return plmnsFromCarrierConfig.toArray(new String[plmnsFromCarrierConfig.size()]); 258 } 259 260 // There are three sources of plmns - sim plmn, plmn list from carrier config and 261 // Ehplmn list from subscription info. 262 // The plmns are prioritized as follows: 263 // 1. Sim plmn 264 // 2. Plmns common to both lists. 265 // 3. Remaining plmns in the lists. 266 List<String> combinedList = new ArrayList<>(); 267 // Get MCCMNC from IMSI 268 String plmnFromImsi = 269 new StringBuilder() 270 .append(subInfo.getMccString()) 271 .append("-") 272 .append(subInfo.getMncString()) 273 .toString(); 274 combinedList.add(plmnFromImsi); 275 276 // Get Ehplmns from TelephonyManager 277 for (String ehplmn : getEhplmns()) { 278 if (ehplmn.length() == 5 || ehplmn.length() == 6) { 279 StringBuilder str = new StringBuilder(ehplmn); 280 str.insert(3, "-"); 281 plmnsFromSubInfo.add(str.toString()); 282 } 283 } 284 285 Log.d(TAG, "plmnsFromSubInfo:" + plmnsFromSubInfo); 286 287 // To avoid double adding plmn from imsi 288 plmnsFromCarrierConfig.removeIf(i -> i.equals(plmnFromImsi)); 289 plmnsFromSubInfo.removeIf(i -> i.equals(plmnFromImsi)); 290 291 for (Iterator<String> iterator = plmnsFromCarrierConfig.iterator(); iterator.hasNext(); ) { 292 String plmn = iterator.next(); 293 if (plmnsFromSubInfo.contains(plmn)) { 294 combinedList.add(plmn); 295 plmnsFromSubInfo.remove(plmn); 296 iterator.remove(); 297 } 298 } 299 300 combinedList.addAll(plmnsFromSubInfo); 301 combinedList.addAll(plmnsFromCarrierConfig); 302 303 Log.d(TAG, "Final plmn list:" + combinedList); 304 return combinedList.toArray(new String[combinedList.size()]); 305 } 306 removeDuplicateIp(List<InetAddress> validIpList)307 private ArrayList<InetAddress> removeDuplicateIp(List<InetAddress> validIpList) { 308 ArrayList<InetAddress> resultIpList = new ArrayList<InetAddress>(); 309 310 for (Iterator<InetAddress> iterator = validIpList.iterator(); iterator.hasNext(); ) { 311 InetAddress validIp = iterator.next(); 312 313 if (!resultIpList.contains(validIp)) { 314 resultIpList.add(validIp); 315 } 316 } 317 318 return resultIpList; 319 } 320 splitMccMnc(String plmn)321 private String[] splitMccMnc(String plmn) { 322 String[] mccmnc = plmn.split("-"); 323 mccmnc[1] = String.format("%03d", Integer.parseInt(mccmnc[1])); 324 return mccmnc; 325 } 326 getEhplmns()327 private List<String> getEhplmns() { 328 TelephonyManager mTelephonyManager = mContext.getSystemService(TelephonyManager.class); 329 mTelephonyManager = 330 mTelephonyManager.createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId)); 331 332 if (mTelephonyManager == null) { 333 Log.e(TAG, "TelephonyManager is NULL"); 334 return new ArrayList<String>(); 335 } else { 336 return mTelephonyManager.getEquivalentHomePlmns(); 337 } 338 } 339 resolutionMethodStatic( int filter, List<InetAddress> validIpList, boolean isRoaming, Network network)340 private void resolutionMethodStatic( 341 int filter, List<InetAddress> validIpList, boolean isRoaming, Network network) { 342 String[] domainNames = null; 343 344 Log.d(TAG, "STATIC Method"); 345 346 // Get the static domain names from carrier config 347 // Config obtained in form of a list of domain names separated by 348 // a delimeter is only used for testing purpose. 349 if (!inSameCountry()) { 350 domainNames = 351 getDomainNames( 352 CarrierConfigManager.Iwlan.KEY_EPDG_STATIC_ADDRESS_ROAMING_STRING); 353 } 354 if (domainNames == null 355 && (domainNames = 356 getDomainNames( 357 CarrierConfigManager.Iwlan.KEY_EPDG_STATIC_ADDRESS_STRING)) 358 == null) { 359 Log.d(TAG, "Static address string is null"); 360 return; 361 } 362 363 Log.d(TAG, "Static Domain Names: " + Arrays.toString(domainNames)); 364 for (String domainName : domainNames) { 365 getIP(domainName, filter, validIpList, network); 366 } 367 } 368 getDomainNames(String key)369 private String[] getDomainNames(String key) { 370 String configValue = (String) IwlanHelper.getConfig(key, mContext, mSlotId); 371 if (configValue == null || configValue.isEmpty()) { 372 Log.d(TAG, key + " string is null"); 373 return null; 374 } 375 return configValue.split(","); 376 } 377 inSameCountry()378 private boolean inSameCountry() { 379 boolean inSameCountry = true; 380 381 TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); 382 tm = tm.createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId)); 383 384 if (tm != null) { 385 String simCountry = tm.getSimCountryIso(); 386 String currentCountry = IwlanHelper.getLastKnownCountryCode(mContext); 387 if (!TextUtils.isEmpty(simCountry) && !TextUtils.isEmpty(currentCountry)) { 388 Log.d(TAG, "simCountry = " + simCountry + ", currentCountry = " + currentCountry); 389 inSameCountry = simCountry.equalsIgnoreCase(currentCountry); 390 } 391 } 392 393 return inSameCountry; 394 } 395 resolutionMethodPlmn( int filter, List<InetAddress> validIpList, boolean isEmergency, Network network)396 private Map<String, List<InetAddress>> resolutionMethodPlmn( 397 int filter, List<InetAddress> validIpList, boolean isEmergency, Network network) { 398 String[] plmnList; 399 StringBuilder domainName = new StringBuilder(); 400 Map<String, List<InetAddress>> domainNameToIpAddr = new LinkedHashMap<>(); 401 402 Log.d(TAG, "PLMN Method"); 403 404 plmnList = getPlmnList(); 405 for (String plmn : plmnList) { 406 String[] mccmnc = splitMccMnc(plmn); 407 /* 408 * Operator Identifier based ePDG FQDN format: 409 * epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 410 * 411 * Operator Identifier based Emergency ePDG FQDN format: 412 * sos.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 413 */ 414 if (isEmergency) { 415 domainName = new StringBuilder(); 416 domainName 417 .append("sos.") 418 .append("epdg.epc.mnc") 419 .append(mccmnc[1]) 420 .append(".mcc") 421 .append(mccmnc[0]) 422 .append(".pub.3gppnetwork.org"); 423 getIP(domainName.toString(), filter, validIpList, network); 424 domainName.setLength(0); 425 } 426 // For emergency PDN setup, still adding FQDN without "sos" header as second priority 427 // because some operator doesn't support hostname with "sos" prefix. 428 domainName 429 .append("epdg.epc.mnc") 430 .append(mccmnc[1]) 431 .append(".mcc") 432 .append(mccmnc[0]) 433 .append(".pub.3gppnetwork.org"); 434 Map.Entry<String, List<InetAddress>> entry = 435 getIP(domainName.toString(), filter, validIpList, network); 436 domainNameToIpAddr.put(entry.getKey(), entry.getValue()); 437 domainName.setLength(0); 438 } 439 return domainNameToIpAddr; 440 } 441 resolutionMethodCellularLoc( int filter, List<InetAddress> validIpList, boolean isEmergency, Network network)442 private void resolutionMethodCellularLoc( 443 int filter, List<InetAddress> validIpList, boolean isEmergency, Network network) { 444 String[] plmnList; 445 StringBuilder domainName = new StringBuilder(); 446 447 Log.d(TAG, "CELLULAR_LOC Method"); 448 449 TelephonyManager mTelephonyManager = mContext.getSystemService(TelephonyManager.class); 450 mTelephonyManager = 451 mTelephonyManager.createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId)); 452 453 if (mTelephonyManager == null) { 454 Log.e(TAG, "TelephonyManager is NULL"); 455 return; 456 } 457 458 List<CellInfo> cellInfoList = mTelephonyManager.getAllCellInfo(); 459 if (cellInfoList == null) { 460 Log.e(TAG, "cellInfoList is NULL"); 461 return; 462 } 463 464 for (CellInfo cellInfo : cellInfoList) { 465 if (!cellInfo.isRegistered()) { 466 continue; 467 } 468 469 if (cellInfo instanceof CellInfoGsm) { 470 CellIdentityGsm gsmCellId = ((CellInfoGsm) cellInfo).getCellIdentity(); 471 String lacString = String.format("%04x", gsmCellId.getLac()); 472 473 lacDomainNameResolution(filter, validIpList, lacString, isEmergency, network); 474 } else if (cellInfo instanceof CellInfoWcdma) { 475 CellIdentityWcdma wcdmaCellId = ((CellInfoWcdma) cellInfo).getCellIdentity(); 476 String lacString = String.format("%04x", wcdmaCellId.getLac()); 477 478 lacDomainNameResolution(filter, validIpList, lacString, isEmergency, network); 479 } else if (cellInfo instanceof CellInfoLte) { 480 CellIdentityLte lteCellId = ((CellInfoLte) cellInfo).getCellIdentity(); 481 String tacString = String.format("%04x", lteCellId.getTac()); 482 String[] tacSubString = new String[2]; 483 tacSubString[0] = tacString.substring(0, 2); 484 tacSubString[1] = tacString.substring(2); 485 486 plmnList = getPlmnList(); 487 for (String plmn : plmnList) { 488 String[] mccmnc = splitMccMnc(plmn); 489 /** 490 * Tracking Area Identity based ePDG FQDN format: 491 * tac-lb<TAC-low-byte>.tac-hb<TAC-high-byte>.tac. 492 * epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 493 * 494 * <p>Tracking Area Identity based Emergency ePDG FQDN format: 495 * tac-lb<TAC-low-byte>.tac-hb<TAC-highbyte>.tac. 496 * sos.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org" 497 */ 498 domainName 499 .append("tac-lb") 500 .append(tacSubString[1]) 501 .append(".tac-hb") 502 .append(tacSubString[0]); 503 if (isEmergency) { 504 domainName.append(".tac.sos.epdg.epc.mnc"); 505 } else { 506 domainName.append(".tac.epdg.epc.mnc"); 507 } 508 domainName 509 .append(mccmnc[1]) 510 .append(".mcc") 511 .append(mccmnc[0]) 512 .append(".pub.3gppnetwork.org"); 513 getIP(domainName.toString(), filter, validIpList, network); 514 domainName.setLength(0); 515 } 516 } else if (cellInfo instanceof CellInfoNr) { 517 CellIdentityNr nrCellId = 518 (CellIdentityNr) ((CellInfoNr) cellInfo).getCellIdentity(); 519 String tacString = String.format("%06x", nrCellId.getTac()); 520 String[] tacSubString = new String[3]; 521 tacSubString[0] = tacString.substring(0, 2); 522 tacSubString[1] = tacString.substring(2, 4); 523 tacSubString[2] = tacString.substring(4); 524 525 plmnList = getPlmnList(); 526 for (String plmn : plmnList) { 527 String[] mccmnc = splitMccMnc(plmn); 528 /** 529 * 5GS Tracking Area Identity based ePDG FQDN format: 530 * tac-lb<TAC-low-byte>.tac-mb<TAC-middle-byte>.tac-hb<TAC-high-byte>. 531 * 5gstac.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 532 * 533 * <p>5GS Tracking Area Identity based Emergency ePDG FQDN format: 534 * tac-lb<TAC-low-byte>.tac-mb<TAC-middle-byte>.tac-hb<TAC-high-byte>. 535 * 5gstac.sos.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 536 */ 537 domainName 538 .append("tac-lb") 539 .append(tacSubString[2]) 540 .append(".tac-mb") 541 .append(tacSubString[1]) 542 .append(".tac-hb") 543 .append(tacSubString[0]); 544 if (isEmergency) { 545 domainName.append(".5gstac.sos.epdg.epc.mnc"); 546 } else { 547 domainName.append(".5gstac.epdg.epc.mnc"); 548 } 549 domainName 550 .append(mccmnc[1]) 551 .append(".mcc") 552 .append(mccmnc[0]) 553 .append(".pub.3gppnetwork.org"); 554 getIP(domainName.toString(), filter, validIpList, network); 555 domainName.setLength(0); 556 } 557 } else { 558 Log.d(TAG, "This cell doesn't contain LAC/TAC info"); 559 } 560 } 561 } 562 lacDomainNameResolution( int filter, List<InetAddress> validIpList, String lacString, boolean isEmergency, Network network)563 private void lacDomainNameResolution( 564 int filter, 565 List<InetAddress> validIpList, 566 String lacString, 567 boolean isEmergency, 568 Network network) { 569 String[] plmnList; 570 StringBuilder domainName = new StringBuilder(); 571 572 plmnList = getPlmnList(); 573 for (String plmn : plmnList) { 574 String[] mccmnc = splitMccMnc(plmn); 575 /** 576 * Location Area Identity based ePDG FQDN format: 577 * lac<LAC>.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 578 * 579 * <p>Location Area Identity based Emergency ePDG FQDN format: 580 * lac<LAC>.sos.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 581 */ 582 domainName.append("lac").append(lacString); 583 if (isEmergency) { 584 domainName.append(".sos.epdg.epc.mnc"); 585 } else { 586 domainName.append(".epdg.epc.mnc"); 587 } 588 domainName 589 .append(mccmnc[1]) 590 .append(".mcc") 591 .append(mccmnc[0]) 592 .append(".pub.3gppnetwork.org"); 593 594 getIP(domainName.toString(), filter, validIpList, network); 595 domainName.setLength(0); 596 } 597 } 598 resolutionMethodPco(int filter, List<InetAddress> validIpList)599 private void resolutionMethodPco(int filter, List<InetAddress> validIpList) { 600 Log.d(TAG, "PCO Method"); 601 602 int PCO_ID_IPV6 = 603 IwlanHelper.getConfig( 604 CarrierConfigManager.Iwlan.KEY_EPDG_PCO_ID_IPV6_INT, mContext, mSlotId); 605 int PCO_ID_IPV4 = 606 IwlanHelper.getConfig( 607 CarrierConfigManager.Iwlan.KEY_EPDG_PCO_ID_IPV4_INT, mContext, mSlotId); 608 609 switch (filter) { 610 case PROTO_FILTER_IPV4: 611 if (mV4PcoId != PCO_ID_IPV4) { 612 clearPcoData(); 613 } else { 614 getInetAddressWithPcoData(mV4PcoData, validIpList); 615 } 616 break; 617 case PROTO_FILTER_IPV6: 618 if (mV6PcoId != PCO_ID_IPV6) { 619 clearPcoData(); 620 } else { 621 getInetAddressWithPcoData(mV6PcoData, validIpList); 622 } 623 break; 624 case PROTO_FILTER_IPV4V6: 625 if ((mV4PcoId != PCO_ID_IPV4) || (mV6PcoId != PCO_ID_IPV6)) { 626 clearPcoData(); 627 } else { 628 getInetAddressWithPcoData(mV4PcoData, validIpList); 629 getInetAddressWithPcoData(mV6PcoData, validIpList); 630 } 631 break; 632 default: 633 Log.d(TAG, "Invalid ProtoFilter : " + filter); 634 } 635 } 636 getInetAddressWithPcoData(byte[] pcoData, List<InetAddress> validIpList)637 private void getInetAddressWithPcoData(byte[] pcoData, List<InetAddress> validIpList) { 638 InetAddress ipAddress; 639 if (pcoData != null && pcoData.length > 0) { 640 try { 641 ipAddress = InetAddress.getByAddress(pcoData); 642 validIpList.add(ipAddress); 643 } catch (Exception e) { 644 Log.e(TAG, "Exception when querying IP address : " + e); 645 } 646 } else { 647 Log.d(TAG, "Empty PCO data"); 648 } 649 } 650 composeFqdnWithMccMnc(String mcc, String mnc, boolean isEmergency)651 private String composeFqdnWithMccMnc(String mcc, String mnc, boolean isEmergency) { 652 StringBuilder domainName = new StringBuilder(); 653 654 /* 655 * Operator Identifier based ePDG FQDN format: 656 * epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 657 * 658 * Operator Identifier based Emergency ePDG FQDN format: 659 * sos.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 660 */ 661 domainName.setLength(0); 662 if (isEmergency) { 663 domainName.append("sos."); 664 } 665 domainName 666 .append("epdg.epc.mnc") 667 .append(mnc) 668 .append(".mcc") 669 .append(mcc) 670 .append(".pub.3gppnetwork.org"); 671 672 return domainName.toString(); 673 } 674 isRegisteredWith3GPP(TelephonyManager telephonyManager)675 private boolean isRegisteredWith3GPP(TelephonyManager telephonyManager) { 676 List<CellInfo> cellInfoList = telephonyManager.getAllCellInfo(); 677 if (cellInfoList == null) { 678 Log.e(TAG, "cellInfoList is NULL"); 679 } else { 680 for (CellInfo cellInfo : cellInfoList) { 681 if (!cellInfo.isRegistered()) { 682 continue; 683 } 684 if (cellInfo instanceof CellInfoGsm 685 || cellInfo instanceof CellInfoTdscdma 686 || cellInfo instanceof CellInfoWcdma 687 || cellInfo instanceof CellInfoLte 688 || cellInfo instanceof CellInfoNr) { 689 return true; 690 } 691 } 692 } 693 return false; 694 } 695 processNaptrResponse( int filter, List<InetAddress> validIpList, boolean isEmergency, Network network, boolean isRegisteredWith3GPP, List<NaptrTarget> naptrResponse, Set<String> plmnsFromCarrierConfig, String registeredhostName)696 private void processNaptrResponse( 697 int filter, 698 List<InetAddress> validIpList, 699 boolean isEmergency, 700 Network network, 701 boolean isRegisteredWith3GPP, 702 List<NaptrTarget> naptrResponse, 703 Set<String> plmnsFromCarrierConfig, 704 String registeredhostName) { 705 Set<String> resultSet = new LinkedHashSet<>(); 706 707 for (NaptrTarget target : naptrResponse) { 708 Log.d(TAG, "NaptrTarget - name: " + target.mName); 709 Log.d(TAG, "NaptrTarget - type: " + target.mType); 710 if (target.mType == NaptrDnsResolver.TYPE_A) { 711 resultSet.add(target.mName); 712 } 713 } 714 715 /* 716 * As 3GPP TS 23.402 4.5.4.5 bullet 2a, 717 * if the device registers via 3GPP and its PLMN info is in the NAPTR response, 718 * try to connect ePDG with this PLMN info. 719 */ 720 if (isRegisteredWith3GPP) { 721 if (resultSet.contains(registeredhostName)) { 722 getIP(registeredhostName, filter, validIpList, network); 723 resultSet.remove(registeredhostName); 724 } 725 } 726 727 /* 728 * As 3GPP TS 23.402 4.5.4.5 bullet 2b 729 * Check if there is any PLMN in both ePDG selection information and the DNS response 730 */ 731 for (String plmn : plmnsFromCarrierConfig) { 732 String[] mccmnc = splitMccMnc(plmn); 733 String carrierConfighostName = composeFqdnWithMccMnc(mccmnc[0], mccmnc[1], isEmergency); 734 735 if (resultSet.contains(carrierConfighostName)) { 736 getIP(carrierConfighostName, filter, validIpList, network); 737 resultSet.remove(carrierConfighostName); 738 } 739 } 740 741 /* 742 * Do FQDN with the remaining PLMNs in the ResultSet 743 */ 744 for (String result : resultSet) { 745 getIP(result, filter, validIpList, network); 746 } 747 } 748 resolutionMethodVisitedCountry( int filter, List<InetAddress> validIpList, boolean isEmergency, Network network)749 private void resolutionMethodVisitedCountry( 750 int filter, List<InetAddress> validIpList, boolean isEmergency, Network network) { 751 StringBuilder domainName = new StringBuilder(); 752 753 TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); 754 telephonyManager = 755 telephonyManager.createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId)); 756 757 if (telephonyManager == null) { 758 Log.e(TAG, "TelephonyManager is NULL"); 759 return; 760 } 761 762 final boolean isRegisteredWith3GPP = isRegisteredWith3GPP(telephonyManager); 763 764 // Get ePDG selection information from CarrierConfig 765 final Set<String> plmnsFromCarrierConfig = 766 new LinkedHashSet<>( 767 Arrays.asList( 768 IwlanHelper.getConfig( 769 CarrierConfigManager.Iwlan.KEY_MCC_MNCS_STRING_ARRAY, 770 mContext, 771 mSlotId))); 772 773 final String cellMcc = telephonyManager.getNetworkOperator().substring(0, 3); 774 final String cellMnc = telephonyManager.getNetworkOperator().substring(3); 775 final String plmnFromNetwork = 776 new StringBuilder().append(cellMcc).append("-").append(cellMnc).toString(); 777 final String registeredhostName = composeFqdnWithMccMnc(cellMcc, cellMnc, isEmergency); 778 779 /* 780 * As TS 23 402 4.5.4.4 bullet 3a 781 * If the UE determines to be located in a country other than its home country 782 * If the UE is registered via 3GPP access to a PLMN and this PLMN matches an entry 783 in the ePDG selection information, then the UE shall select an ePDG in this PLMN. 784 */ 785 if (isRegisteredWith3GPP) { 786 if (plmnsFromCarrierConfig.contains(plmnFromNetwork)) { 787 getIP(registeredhostName, filter, validIpList, network); 788 } 789 } 790 791 /* 792 * Visited Country FQDN format: 793 * epdg.epc.mcc<MCC>.visited-country.pub.3gppnetwork.org 794 * 795 * Visited Country Emergency ePDG FQDN format: 796 * sos.epdg.epc.mcc<MCC>.visited-country.pub.3gppnetwork.org 797 */ 798 if (isEmergency) { 799 domainName.append("sos."); 800 } 801 domainName 802 .append("epdg.epc.mcc") 803 .append(cellMcc) 804 .append(".visited-country.pub.3gppnetwork.org"); 805 806 Log.d(TAG, "Visited Country FQDN with " + domainName.toString()); 807 808 CompletableFuture<List<NaptrTarget>> naptrDnsResult = new CompletableFuture<>(); 809 DnsResolver.Callback<List<NaptrTarget>> naptrDnsCb = 810 new DnsResolver.Callback<List<NaptrTarget>>() { 811 @Override 812 public void onAnswer(@NonNull final List<NaptrTarget> answer, final int rcode) { 813 if (rcode == 0 && answer.size() != 0) { 814 naptrDnsResult.complete(answer); 815 } else { 816 naptrDnsResult.completeExceptionally(new UnknownHostException()); 817 } 818 } 819 820 @Override 821 public void onError(@Nullable final DnsException error) { 822 naptrDnsResult.completeExceptionally(error); 823 } 824 }; 825 NaptrDnsResolver.query(network, domainName.toString(), r -> r.run(), null, naptrDnsCb); 826 827 try { 828 final List<NaptrTarget> naptrResponse = 829 naptrDnsResult.get(DNS_RESOLVER_TIMEOUT_DURATION_SEC, TimeUnit.SECONDS); 830 // Check if there is any record in the NAPTR response 831 if (naptrResponse != null && naptrResponse.size() > 0) { 832 processNaptrResponse( 833 filter, 834 validIpList, 835 isEmergency, 836 network, 837 isRegisteredWith3GPP, 838 naptrResponse, 839 plmnsFromCarrierConfig, 840 registeredhostName); 841 } 842 } catch (ExecutionException e) { 843 Log.e(TAG, "Cause of ExecutionException: ", e.getCause()); 844 } catch (InterruptedException e) { 845 if (Thread.currentThread().interrupted()) { 846 Thread.currentThread().interrupt(); 847 } 848 Log.e(TAG, "InterruptedException: ", e); 849 } catch (TimeoutException e) { 850 Log.e(TAG, "TimeoutException: ", e); 851 } 852 } 853 getValidatedServerList( int transactionId, @ProtoFilter int filter, boolean isRoaming, boolean isEmergency, @NonNull Network network, EpdgSelectorCallback selectorCallback)854 public IwlanError getValidatedServerList( 855 int transactionId, 856 @ProtoFilter int filter, 857 boolean isRoaming, 858 boolean isEmergency, 859 @NonNull Network network, 860 EpdgSelectorCallback selectorCallback) { 861 862 Runnable doValidation = 863 () -> { 864 List<InetAddress> validIpList = new ArrayList<>(); 865 Log.d(TAG, "Processing request with transactionId: " + transactionId); 866 867 int[] addrResolutionMethods = 868 IwlanHelper.getConfig( 869 CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, 870 mContext, 871 mSlotId); 872 873 final boolean isVisitedCountryMethodRequired = 874 Arrays.stream(addrResolutionMethods) 875 .anyMatch( 876 i -> 877 i 878 == CarrierConfigManager.Iwlan 879 .EPDG_ADDRESS_VISITED_COUNTRY); 880 881 // In the visited country 882 if (isRoaming && !inSameCountry() && isVisitedCountryMethodRequired) { 883 resolutionMethodVisitedCountry(filter, validIpList, isEmergency, network); 884 } 885 886 Map<String, List<InetAddress>> plmnDomainNamesToIpAddress = null; 887 for (int addrResolutionMethod : addrResolutionMethods) { 888 switch (addrResolutionMethod) { 889 case CarrierConfigManager.Iwlan.EPDG_ADDRESS_STATIC: 890 resolutionMethodStatic(filter, validIpList, isRoaming, network); 891 break; 892 893 case CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN: 894 plmnDomainNamesToIpAddress = 895 resolutionMethodPlmn( 896 filter, validIpList, isEmergency, network); 897 break; 898 899 case CarrierConfigManager.Iwlan.EPDG_ADDRESS_PCO: 900 resolutionMethodPco(filter, validIpList); 901 break; 902 903 case CarrierConfigManager.Iwlan.EPDG_ADDRESS_CELLULAR_LOC: 904 resolutionMethodCellularLoc( 905 filter, validIpList, isEmergency, network); 906 break; 907 908 default: 909 Log.d( 910 TAG, 911 "Incorrect address resolution method " 912 + addrResolutionMethod); 913 } 914 } 915 916 if (selectorCallback != null) { 917 if (mErrorPolicyManager.getMostRecentDataFailCause() 918 == DataFailCause.IWLAN_CONGESTION) { 919 int numFqdns = plmnDomainNamesToIpAddress.size(); 920 int index = mErrorPolicyManager.getCurrentFqdnIndex(numFqdns); 921 if (index >= 0 && index < numFqdns) { 922 Object[] keys = plmnDomainNamesToIpAddress.keySet().toArray(); 923 validIpList = plmnDomainNamesToIpAddress.get(keys[index]); 924 } else { 925 Log.w( 926 TAG, 927 "CONGESTION error handling- invalid index: " 928 + index 929 + " number of PLMN FQDNs: " 930 + numFqdns); 931 } 932 } 933 934 if (!validIpList.isEmpty()) { 935 Collections.sort(validIpList, inetAddressComparator); 936 selectorCallback.onServerListChanged( 937 transactionId, removeDuplicateIp(validIpList)); 938 } else { 939 selectorCallback.onError( 940 transactionId, 941 new IwlanError( 942 IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED)); 943 } 944 } 945 }; 946 Thread subThread = new Thread(doValidation); 947 subThread.start(); 948 return new IwlanError(IwlanError.NO_ERROR); 949 } 950 } 951