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.ArrayBlockingQueue; 58 import java.util.concurrent.BlockingQueue; 59 import java.util.concurrent.CompletableFuture; 60 import java.util.concurrent.ConcurrentHashMap; 61 import java.util.concurrent.ExecutionException; 62 import java.util.concurrent.Executor; 63 import java.util.concurrent.ExecutorService; 64 import java.util.concurrent.Future; 65 import java.util.concurrent.SynchronousQueue; 66 import java.util.concurrent.ThreadPoolExecutor; 67 import java.util.concurrent.TimeUnit; 68 import java.util.concurrent.TimeoutException; 69 import java.util.stream.Collectors; 70 71 public class EpdgSelector { 72 private static final String TAG = "EpdgSelector"; 73 private final Context mContext; 74 private final int mSlotId; 75 private static final ConcurrentHashMap<Integer, EpdgSelector> mSelectorInstances = 76 new ConcurrentHashMap<>(); 77 private int mV4PcoId = -1; 78 private int mV6PcoId = -1; 79 private byte[] mV4PcoData = null; 80 private byte[] mV6PcoData = null; 81 @NonNull private final ErrorPolicyManager mErrorPolicyManager; 82 83 // The default DNS timeout in the DNS module is set to 5 seconds. To account for IPC overhead, 84 // IWLAN applies an internal timeout of 6 seconds, slightly longer than the default timeout 85 private static final long DNS_RESOLVER_TIMEOUT_DURATION_SEC = 6L; 86 87 private static final long PARALLEL_STATIC_RESOLUTION_TIMEOUT_DURATION_SEC = 6L; 88 private static final long PARALLEL_PLMN_RESOLUTION_TIMEOUT_DURATION_SEC = 20L; 89 private static final int NUM_EPDG_SELECTION_EXECUTORS = 2; // 1 each for normal selection, SOS. 90 private static final int MAX_EPDG_SELECTION_THREADS = 2; // 1 each for prefetch, tunnel bringup. 91 private static final int MAX_DNS_RESOLVER_THREADS = 25; // Do not expect > 25 FQDNs per carrier. 92 private static final String NO_DOMAIN = "NO_DOMAIN"; 93 94 BlockingQueue<Runnable> dnsResolutionQueue = 95 new ArrayBlockingQueue<>( 96 MAX_DNS_RESOLVER_THREADS 97 * MAX_EPDG_SELECTION_THREADS 98 * NUM_EPDG_SELECTION_EXECUTORS); 99 100 Executor mDnsResolutionExecutor = 101 new ThreadPoolExecutor( 102 0, MAX_DNS_RESOLVER_THREADS, 60L, TimeUnit.SECONDS, dnsResolutionQueue); 103 104 ExecutorService mEpdgSelectionExecutor = 105 new ThreadPoolExecutor( 106 0, 107 MAX_EPDG_SELECTION_THREADS, 108 60L, 109 TimeUnit.SECONDS, 110 new SynchronousQueue<Runnable>()); 111 Future mDnsPrefetchFuture; 112 113 ExecutorService mSosEpdgSelectionExecutor = 114 new ThreadPoolExecutor( 115 0, 116 MAX_EPDG_SELECTION_THREADS, 117 60L, 118 TimeUnit.SECONDS, 119 new SynchronousQueue<Runnable>()); 120 Future mSosDnsPrefetchFuture; 121 122 final Comparator<InetAddress> inetAddressComparator = 123 (ip1, ip2) -> { 124 if ((ip1 instanceof Inet4Address) && (ip2 instanceof Inet6Address)) { 125 return -1; 126 } else if ((ip1 instanceof Inet6Address) && (ip2 instanceof Inet4Address)) { 127 return 1; 128 } else { 129 return 0; 130 } 131 }; 132 133 public static final int PROTO_FILTER_IPV4 = 0; 134 public static final int PROTO_FILTER_IPV6 = 1; 135 public static final int PROTO_FILTER_IPV4V6 = 2; 136 137 @IntDef({PROTO_FILTER_IPV4, PROTO_FILTER_IPV6, PROTO_FILTER_IPV4V6}) 138 @interface ProtoFilter {} 139 140 public static final int IPV4_PREFERRED = 0; 141 public static final int IPV6_PREFERRED = 1; 142 public static final int SYSTEM_PREFERRED = 2; 143 144 @IntDef({IPV4_PREFERRED, IPV6_PREFERRED, SYSTEM_PREFERRED}) 145 @interface EpdgAddressOrder {} 146 147 public interface EpdgSelectorCallback { 148 /*gives priority ordered list of addresses*/ onServerListChanged(int transactionId, List<InetAddress> validIPList)149 void onServerListChanged(int transactionId, List<InetAddress> validIPList); 150 onError(int transactionId, IwlanError error)151 void onError(int transactionId, IwlanError error); 152 } 153 154 @VisibleForTesting EpdgSelector(Context context, int slotId)155 EpdgSelector(Context context, int slotId) { 156 mContext = context; 157 mSlotId = slotId; 158 159 mErrorPolicyManager = ErrorPolicyManager.getInstance(mContext, mSlotId); 160 } 161 getSelectorInstance(Context context, int slotId)162 public static EpdgSelector getSelectorInstance(Context context, int slotId) { 163 mSelectorInstances.computeIfAbsent(slotId, k -> new EpdgSelector(context, slotId)); 164 return mSelectorInstances.get(slotId); 165 } 166 setPcoData(int pcoId, byte[] pcoData)167 public boolean setPcoData(int pcoId, byte[] pcoData) { 168 Log.d( 169 TAG, 170 "onReceive PcoId:" 171 + String.format("0x%04x", pcoId) 172 + " PcoData:" 173 + Arrays.toString(pcoData)); 174 175 int PCO_ID_IPV6 = 176 IwlanHelper.getConfig( 177 CarrierConfigManager.Iwlan.KEY_EPDG_PCO_ID_IPV6_INT, mContext, mSlotId); 178 int PCO_ID_IPV4 = 179 IwlanHelper.getConfig( 180 CarrierConfigManager.Iwlan.KEY_EPDG_PCO_ID_IPV4_INT, mContext, mSlotId); 181 182 Log.d( 183 TAG, 184 "PCO_ID_IPV6:" 185 + String.format("0x%04x", PCO_ID_IPV6) 186 + " PCO_ID_IPV4:" 187 + String.format("0x%04x", PCO_ID_IPV4)); 188 189 if (pcoId == PCO_ID_IPV4) { 190 mV4PcoId = pcoId; 191 mV4PcoData = pcoData; 192 return true; 193 } else if (pcoId == PCO_ID_IPV6) { 194 mV6PcoId = pcoId; 195 mV6PcoData = pcoData; 196 return true; 197 } 198 199 return false; 200 } 201 clearPcoData()202 public void clearPcoData() { 203 Log.d(TAG, "Clear PCO data"); 204 mV4PcoId = -1; 205 mV6PcoId = -1; 206 mV4PcoData = null; 207 mV6PcoData = null; 208 } 209 submitDnsResolverQuery( String domainName, Network network, int queryType, Executor executor)210 private CompletableFuture<Map.Entry<String, List<InetAddress>>> submitDnsResolverQuery( 211 String domainName, Network network, int queryType, Executor executor) { 212 CompletableFuture<Map.Entry<String, List<InetAddress>>> result = new CompletableFuture(); 213 214 final DnsResolver.Callback<List<InetAddress>> cb = 215 new DnsResolver.Callback<List<InetAddress>>() { 216 @Override 217 public void onAnswer(@NonNull final List<InetAddress> answer, final int rcode) { 218 if (rcode != 0) { 219 Log.e( 220 TAG, 221 "DnsResolver Response Code = " 222 + rcode 223 + " for domain " 224 + domainName); 225 } 226 Map.Entry<String, List<InetAddress>> entry = Map.entry(domainName, answer); 227 result.complete(entry); 228 } 229 230 @Override 231 public void onError(@Nullable final DnsResolver.DnsException error) { 232 Log.e( 233 TAG, 234 "Resolve DNS with error: " + error + " for domain: " + domainName); 235 result.complete(null); 236 } 237 }; 238 DnsResolver.getInstance() 239 .query(network, domainName, queryType, DnsResolver.FLAG_EMPTY, executor, null, cb); 240 return result; 241 } 242 v4v6ProtocolFilter(List<InetAddress> ipList, int filter)243 private List<InetAddress> v4v6ProtocolFilter(List<InetAddress> ipList, int filter) { 244 List<InetAddress> validIpList = new ArrayList<>(); 245 for (InetAddress ipAddress : ipList) { 246 if (IwlanHelper.isIpv4EmbeddedIpv6Address(ipAddress)) { 247 continue; 248 } 249 switch (filter) { 250 case PROTO_FILTER_IPV4: 251 if (ipAddress instanceof Inet4Address) { 252 validIpList.add(ipAddress); 253 } 254 break; 255 case PROTO_FILTER_IPV6: 256 if (ipAddress instanceof Inet6Address) { 257 validIpList.add(ipAddress); 258 } 259 break; 260 case PROTO_FILTER_IPV4V6: 261 validIpList.add(ipAddress); 262 break; 263 default: 264 Log.d(TAG, "Invalid ProtoFilter : " + filter); 265 } 266 } 267 return validIpList; 268 } 269 270 // Converts a list of CompletableFutures of type T into a single CompletableFuture containing a 271 // list of T. The resulting CompletableFuture waits for all futures to complete, 272 // even if any future throw an exception. allOf(List<CompletableFuture<T>> futuresList)273 private <T> CompletableFuture<List<T>> allOf(List<CompletableFuture<T>> futuresList) { 274 CompletableFuture<Void> allFuturesResult = 275 CompletableFuture.allOf( 276 futuresList.toArray(new CompletableFuture[futuresList.size()])); 277 return allFuturesResult.thenApply( 278 v -> 279 futuresList.stream() 280 .map(CompletableFuture::join) 281 .filter(Objects::nonNull) 282 .collect(Collectors.<T>toList())); 283 } 284 285 @VisibleForTesting hasIpv4Address(Network network)286 protected boolean hasIpv4Address(Network network) { 287 return IwlanHelper.hasIpv4Address(IwlanHelper.getAllAddressesForNetwork(network, mContext)); 288 } 289 290 @VisibleForTesting hasIpv6Address(Network network)291 protected boolean hasIpv6Address(Network network) { 292 return IwlanHelper.hasIpv6Address(IwlanHelper.getAllAddressesForNetwork(network, mContext)); 293 } 294 printParallelDnsResult(Map<String, List<InetAddress>> domainNameToIpAddresses)295 private void printParallelDnsResult(Map<String, List<InetAddress>> domainNameToIpAddresses) { 296 Log.d(TAG, "Parallel DNS resolution result:"); 297 for (String domain : domainNameToIpAddresses.keySet()) { 298 Log.d(TAG, domain + ": " + domainNameToIpAddresses.get(domain)); 299 } 300 } 301 /** 302 * Returns a list of unique IP addresses corresponding to the given domain names, in the same 303 * order of the input. Runs DNS resolution across parallel threads. 304 * 305 * @param domainNames Domain names for which DNS resolution needs to be performed. 306 * @param filter Selects for IPv4, IPv6 (or both) addresses from the resulting DNS records 307 * @param network {@link Network} Network on which to run the DNS query. 308 * @param timeout timeout in seconds. 309 * @return List of unique IP addresses corresponding to the domainNames. 310 */ getIP( List<String> domainNames, int filter, Network network, long timeout)311 private LinkedHashMap<String, List<InetAddress>> getIP( 312 List<String> domainNames, int filter, Network network, long timeout) { 313 // LinkedHashMap preserves insertion order (and hence priority) of domain names passed in. 314 LinkedHashMap<String, List<InetAddress>> domainNameToIpAddr = new LinkedHashMap<>(); 315 316 List<CompletableFuture<Map.Entry<String, List<InetAddress>>>> futuresList = 317 new ArrayList<>(); 318 for (String domainName : domainNames) { 319 if (InetAddresses.isNumericAddress(domainName)) { 320 Log.d(TAG, domainName + " is a numeric IP address!"); 321 InetAddress inetAddr = InetAddresses.parseNumericAddress(domainName); 322 domainNameToIpAddr.put(NO_DOMAIN, new ArrayList<>(List.of(inetAddr))); 323 continue; 324 } 325 326 domainNameToIpAddr.put(domainName, new ArrayList<>()); 327 // Dispatches separate IPv4 and IPv6 queries to avoid being blocked on either result. 328 if (hasIpv4Address(network)) { 329 futuresList.add( 330 submitDnsResolverQuery( 331 domainName, network, DnsResolver.TYPE_A, mDnsResolutionExecutor)); 332 } 333 if (hasIpv6Address(network)) { 334 futuresList.add( 335 submitDnsResolverQuery( 336 domainName, 337 network, 338 DnsResolver.TYPE_AAAA, 339 mDnsResolutionExecutor)); 340 } 341 } 342 CompletableFuture<List<Map.Entry<String, List<InetAddress>>>> allFuturesResult = 343 allOf(futuresList); 344 345 List<Map.Entry<String, List<InetAddress>>> resultList = null; 346 try { 347 resultList = allFuturesResult.get(timeout, TimeUnit.SECONDS); 348 } catch (ExecutionException e) { 349 Log.e(TAG, "Cause of ExecutionException: ", e.getCause()); 350 } catch (InterruptedException e) { 351 Thread.currentThread().interrupt(); 352 Log.e(TAG, "InterruptedException: ", e); 353 } catch (TimeoutException e) { 354 Log.e(TAG, "TimeoutException: ", e); 355 } finally { 356 if (resultList == null) { 357 Log.w(TAG, "No IP addresses in parallel DNS query!"); 358 } else { 359 for (Map.Entry<String, List<InetAddress>> entry : resultList) { 360 String resultDomainName = entry.getKey(); 361 List<InetAddress> resultIpAddr = v4v6ProtocolFilter(entry.getValue(), filter); 362 363 if (!domainNameToIpAddr.containsKey(resultDomainName)) { 364 Log.w( 365 TAG, 366 "Unexpected domain name in DnsResolver result: " 367 + resultDomainName); 368 continue; 369 } 370 domainNameToIpAddr.get(resultDomainName).addAll(resultIpAddr); 371 } 372 } 373 } 374 return domainNameToIpAddr; 375 } 376 377 /** 378 * Updates the validIpList with the IP addresses corresponding to this domainName. Runs blocking 379 * DNS resolution on the same thread. 380 * 381 * @param domainName Domain name for which DNS resolution needs to be performed. 382 * @param filter Selects for IPv4, IPv6 (or both) addresses from the resulting DNS records 383 * @param validIpList A running list of IP addresses that needs to be updated. 384 * @param network {@link Network} Network on which to run the DNS query. 385 */ getIP( String domainName, int filter, List<InetAddress> validIpList, Network network)386 private void getIP( 387 String domainName, int filter, List<InetAddress> validIpList, Network network) { 388 List<InetAddress> ipList = new ArrayList<InetAddress>(); 389 390 // Get All IP for each domain name 391 Log.d(TAG, "Input domainName : " + domainName); 392 393 if (InetAddresses.isNumericAddress(domainName)) { 394 Log.d(TAG, domainName + " is a numeric IP address!"); 395 ipList.add(InetAddresses.parseNumericAddress(domainName)); 396 } else { 397 try { 398 CompletableFuture<List<InetAddress>> result = new CompletableFuture(); 399 final DnsResolver.Callback<List<InetAddress>> cb = 400 new DnsResolver.Callback<List<InetAddress>>() { 401 @Override 402 public void onAnswer( 403 @NonNull final List<InetAddress> answer, final int rcode) { 404 if (rcode != 0) { 405 Log.e(TAG, "DnsResolver Response Code = " + rcode); 406 } 407 result.complete(answer); 408 } 409 410 @Override 411 public void onError(@Nullable final DnsResolver.DnsException error) { 412 Log.e(TAG, "Resolve DNS with error : " + error); 413 result.completeExceptionally(error); 414 } 415 }; 416 DnsResolver.getInstance() 417 .query( 418 network, 419 domainName, 420 DnsResolver.FLAG_EMPTY, 421 Runnable::run, 422 null, 423 cb); 424 ipList = 425 new ArrayList<>( 426 result.get(DNS_RESOLVER_TIMEOUT_DURATION_SEC, TimeUnit.SECONDS)); 427 } catch (ExecutionException e) { 428 Log.e(TAG, "Cause of ExecutionException: ", e.getCause()); 429 } catch (InterruptedException e) { 430 Thread thread = Thread.currentThread(); 431 if (thread.interrupted()) { 432 thread.interrupt(); 433 } 434 Log.e(TAG, "InterruptedException: ", e); 435 } catch (TimeoutException e) { 436 Log.e(TAG, "TimeoutException: ", e); 437 } 438 } 439 440 List<InetAddress> filteredIpList = v4v6ProtocolFilter(ipList, filter); 441 validIpList.addAll(filteredIpList); 442 } 443 getPlmnList()444 private String[] getPlmnList() { 445 List<String> plmnsFromCarrierConfig = getPlmnsFromCarrierConfig(); 446 Log.d(TAG, "plmnsFromCarrierConfig:" + plmnsFromCarrierConfig); 447 448 // Get Ehplmns & mccmnc from SubscriptionManager 449 SubscriptionManager subscriptionManager = 450 mContext.getSystemService(SubscriptionManager.class); 451 if (subscriptionManager == null) { 452 Log.e(TAG, "SubscriptionManager is NULL"); 453 return plmnsFromCarrierConfig.toArray(new String[plmnsFromCarrierConfig.size()]); 454 } 455 456 SubscriptionInfo subInfo = 457 subscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(mSlotId); 458 if (subInfo == null) { 459 Log.e(TAG, "SubscriptionInfo is NULL"); 460 return plmnsFromCarrierConfig.toArray(new String[plmnsFromCarrierConfig.size()]); 461 } 462 463 // Get MCCMNC from IMSI 464 String plmnFromImsi = subInfo.getMccString() + subInfo.getMncString(); 465 466 int[] prioritizedPlmnTypes = 467 IwlanHelper.getConfig( 468 CarrierConfigManager.Iwlan.KEY_EPDG_PLMN_PRIORITY_INT_ARRAY, 469 mContext, 470 mSlotId); 471 472 List<String> ehplmns = getEhplmns(); 473 String registeredPlmn = getRegisteredPlmn(); 474 475 List<String> combinedList = new ArrayList<>(); 476 for (int plmnType : prioritizedPlmnTypes) { 477 switch (plmnType) { 478 case CarrierConfigManager.Iwlan.EPDG_PLMN_RPLMN: 479 if (isInEpdgSelectionInfo(registeredPlmn)) { 480 combinedList.add(registeredPlmn); 481 } 482 break; 483 case CarrierConfigManager.Iwlan.EPDG_PLMN_HPLMN: 484 combinedList.add(plmnFromImsi); 485 break; 486 case CarrierConfigManager.Iwlan.EPDG_PLMN_EHPLMN_ALL: 487 combinedList.addAll(getEhplmns()); 488 break; 489 case CarrierConfigManager.Iwlan.EPDG_PLMN_EHPLMN_FIRST: 490 if (!ehplmns.isEmpty()) { 491 combinedList.add(ehplmns.get(0)); 492 } 493 break; 494 default: 495 Log.e(TAG, "Unknown PLMN type: " + plmnType); 496 break; 497 } 498 } 499 500 combinedList = 501 combinedList.stream() 502 .distinct() 503 .filter(EpdgSelector::isValidPlmn) 504 .map(plmn -> new StringBuilder(plmn).insert(3, "-").toString()) 505 .toList(); 506 507 Log.d(TAG, "Final plmn list:" + combinedList); 508 return combinedList.toArray(new String[combinedList.size()]); 509 } 510 getPlmnsFromCarrierConfig()511 private List<String> getPlmnsFromCarrierConfig() { 512 return Arrays.asList( 513 IwlanHelper.getConfig( 514 CarrierConfigManager.Iwlan.KEY_MCC_MNCS_STRING_ARRAY, mContext, mSlotId)); 515 } 516 isInEpdgSelectionInfo(String plmn)517 private boolean isInEpdgSelectionInfo(String plmn) { 518 if (!isValidPlmn(plmn)) { 519 return false; 520 } 521 List<String> plmnsFromCarrierConfig = getPlmnsFromCarrierConfig(); 522 return plmnsFromCarrierConfig.contains(new StringBuilder(plmn).insert(3, "-").toString()); 523 } 524 removeDuplicateIp(List<InetAddress> validIpList)525 private ArrayList<InetAddress> removeDuplicateIp(List<InetAddress> validIpList) { 526 ArrayList<InetAddress> resultIpList = new ArrayList<InetAddress>(); 527 528 for (InetAddress validIp : validIpList) { 529 if (!resultIpList.contains(validIp)) { 530 resultIpList.add(validIp); 531 } 532 } 533 534 return resultIpList; 535 } 536 prioritizeIp(@onNull List<InetAddress> validIpList, @EpdgAddressOrder int order)537 private void prioritizeIp(@NonNull List<InetAddress> validIpList, @EpdgAddressOrder int order) { 538 switch (order) { 539 case IPV4_PREFERRED: 540 validIpList.sort(inetAddressComparator); 541 break; 542 case IPV6_PREFERRED: 543 validIpList.sort(inetAddressComparator.reversed()); 544 break; 545 case SYSTEM_PREFERRED: 546 break; 547 default: 548 Log.w(TAG, "Invalid EpdgAddressOrder : " + order); 549 } 550 } 551 splitMccMnc(String plmn)552 private String[] splitMccMnc(String plmn) { 553 String[] mccmnc = plmn.split("-"); 554 mccmnc[1] = String.format("%03d", Integer.parseInt(mccmnc[1])); 555 return mccmnc; 556 } 557 558 /** 559 * @return the registered PLMN, null if not registered with 3gpp or failed to get telephony 560 * manager 561 */ 562 @Nullable getRegisteredPlmn()563 private String getRegisteredPlmn() { 564 TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); 565 if (telephonyManager == null) { 566 Log.e(TAG, "TelephonyManager is NULL"); 567 return null; 568 } 569 570 telephonyManager = 571 telephonyManager.createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId)); 572 573 String registeredPlmn = telephonyManager.getNetworkOperator(); 574 return registeredPlmn.isEmpty() ? null : registeredPlmn; 575 } 576 getEhplmns()577 private List<String> getEhplmns() { 578 TelephonyManager mTelephonyManager = mContext.getSystemService(TelephonyManager.class); 579 mTelephonyManager = 580 Objects.requireNonNull(mTelephonyManager) 581 .createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId)); 582 583 if (mTelephonyManager == null) { 584 Log.e(TAG, "TelephonyManager is NULL"); 585 return new ArrayList<String>(); 586 } else { 587 return mTelephonyManager.getEquivalentHomePlmns(); 588 } 589 } 590 resolutionMethodStatic( int filter, List<InetAddress> validIpList, Network network)591 private void resolutionMethodStatic( 592 int filter, List<InetAddress> validIpList, Network network) { 593 String[] domainNames = null; 594 595 Log.d(TAG, "STATIC Method"); 596 597 // Get the static domain names from carrier config 598 // Config obtained in form of a list of domain names separated by 599 // a delimiter is only used for testing purpose. 600 if (!inSameCountry()) { 601 domainNames = 602 getDomainNames( 603 CarrierConfigManager.Iwlan.KEY_EPDG_STATIC_ADDRESS_ROAMING_STRING); 604 } 605 if (domainNames == null 606 && (domainNames = 607 getDomainNames( 608 CarrierConfigManager.Iwlan.KEY_EPDG_STATIC_ADDRESS_STRING)) 609 == null) { 610 Log.d(TAG, "Static address string is null"); 611 return; 612 } 613 614 Log.d(TAG, "Static Domain Names: " + Arrays.toString(domainNames)); 615 LinkedHashMap<String, List<InetAddress>> domainNameToIpAddr = 616 getIP( 617 Arrays.asList(domainNames), 618 filter, 619 network, 620 PARALLEL_STATIC_RESOLUTION_TIMEOUT_DURATION_SEC); 621 printParallelDnsResult(domainNameToIpAddr); 622 domainNameToIpAddr.values().forEach(validIpList::addAll); 623 } 624 getDomainNames(String key)625 private String[] getDomainNames(String key) { 626 String configValue = (String) IwlanHelper.getConfig(key, mContext, mSlotId); 627 if (configValue == null || configValue.isEmpty()) { 628 Log.d(TAG, key + " string is null"); 629 return null; 630 } 631 return configValue.split(","); 632 } 633 inSameCountry()634 private boolean inSameCountry() { 635 boolean inSameCountry = true; 636 637 TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); 638 tm = 639 Objects.requireNonNull(tm) 640 .createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId)); 641 642 if (tm != null) { 643 String simCountry = tm.getSimCountryIso(); 644 String currentCountry = IwlanHelper.getLastKnownCountryCode(mContext); 645 if (!TextUtils.isEmpty(simCountry) && !TextUtils.isEmpty(currentCountry)) { 646 Log.d(TAG, "simCountry = " + simCountry + ", currentCountry = " + currentCountry); 647 inSameCountry = simCountry.equalsIgnoreCase(currentCountry); 648 } 649 } 650 651 return inSameCountry; 652 } 653 resolutionMethodPlmn( int filter, List<InetAddress> validIpList, boolean isEmergency, Network network)654 private Map<String, List<InetAddress>> resolutionMethodPlmn( 655 int filter, List<InetAddress> validIpList, boolean isEmergency, Network network) { 656 String[] plmnList; 657 StringBuilder domainName = new StringBuilder(); 658 659 Log.d(TAG, "PLMN Method"); 660 661 plmnList = getPlmnList(); 662 List<String> domainNames = new ArrayList<>(); 663 for (String plmn : plmnList) { 664 String[] mccmnc = splitMccMnc(plmn); 665 /* 666 * Operator Identifier based ePDG FQDN format: 667 * epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 668 * 669 * Operator Identifier based Emergency ePDG FQDN format: 670 * sos.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 671 */ 672 if (isEmergency) { 673 domainName = new StringBuilder(); 674 domainName 675 .append("sos.") 676 .append("epdg.epc.mnc") 677 .append(mccmnc[1]) 678 .append(".mcc") 679 .append(mccmnc[0]) 680 .append(".pub.3gppnetwork.org"); 681 domainNames.add(domainName.toString()); 682 domainName.setLength(0); 683 } 684 // For emergency PDN setup, still adding FQDN without "sos" header as second priority 685 // because some operator doesn't support hostname with "sos" prefix. 686 domainName 687 .append("epdg.epc.mnc") 688 .append(mccmnc[1]) 689 .append(".mcc") 690 .append(mccmnc[0]) 691 .append(".pub.3gppnetwork.org"); 692 domainNames.add(domainName.toString()); 693 domainName.setLength(0); 694 } 695 696 LinkedHashMap<String, List<InetAddress>> domainNameToIpAddr = 697 getIP(domainNames, filter, network, PARALLEL_PLMN_RESOLUTION_TIMEOUT_DURATION_SEC); 698 printParallelDnsResult(domainNameToIpAddr); 699 domainNameToIpAddr.values().forEach(validIpList::addAll); 700 return domainNameToIpAddr; 701 } 702 resolutionMethodCellularLoc( int filter, List<InetAddress> validIpList, boolean isEmergency, Network network)703 private void resolutionMethodCellularLoc( 704 int filter, List<InetAddress> validIpList, boolean isEmergency, Network network) { 705 String[] plmnList; 706 StringBuilder domainName = new StringBuilder(); 707 708 Log.d(TAG, "CELLULAR_LOC Method"); 709 710 TelephonyManager mTelephonyManager = mContext.getSystemService(TelephonyManager.class); 711 mTelephonyManager = 712 Objects.requireNonNull(mTelephonyManager) 713 .createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId)); 714 715 if (mTelephonyManager == null) { 716 Log.e(TAG, "TelephonyManager is NULL"); 717 return; 718 } 719 720 List<CellInfo> cellInfoList = mTelephonyManager.getAllCellInfo(); 721 if (cellInfoList == null) { 722 Log.e(TAG, "cellInfoList is NULL"); 723 return; 724 } 725 726 for (CellInfo cellInfo : cellInfoList) { 727 if (!cellInfo.isRegistered()) { 728 continue; 729 } 730 731 if (cellInfo instanceof CellInfoGsm) { 732 CellIdentityGsm gsmCellId = ((CellInfoGsm) cellInfo).getCellIdentity(); 733 String lacString = String.format("%04x", gsmCellId.getLac()); 734 735 lacDomainNameResolution(filter, validIpList, lacString, isEmergency, network); 736 } else if (cellInfo instanceof CellInfoWcdma) { 737 CellIdentityWcdma wcdmaCellId = ((CellInfoWcdma) cellInfo).getCellIdentity(); 738 String lacString = String.format("%04x", wcdmaCellId.getLac()); 739 740 lacDomainNameResolution(filter, validIpList, lacString, isEmergency, network); 741 } else if (cellInfo instanceof CellInfoLte) { 742 CellIdentityLte lteCellId = ((CellInfoLte) cellInfo).getCellIdentity(); 743 String tacString = String.format("%04x", lteCellId.getTac()); 744 String[] tacSubString = new String[2]; 745 tacSubString[0] = tacString.substring(0, 2); 746 tacSubString[1] = tacString.substring(2); 747 748 plmnList = getPlmnList(); 749 for (String plmn : plmnList) { 750 String[] mccmnc = splitMccMnc(plmn); 751 /** 752 * Tracking Area Identity based ePDG FQDN format: 753 * tac-lb<TAC-low-byte>.tac-hb<TAC-high-byte>.tac. 754 * epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 755 * 756 * <p>Tracking Area Identity based Emergency ePDG FQDN format: 757 * tac-lb<TAC-low-byte>.tac-hb<TAC-highbyte>.tac. 758 * sos.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org" 759 */ 760 domainName 761 .append("tac-lb") 762 .append(tacSubString[1]) 763 .append(".tac-hb") 764 .append(tacSubString[0]); 765 if (isEmergency) { 766 domainName.append(".tac.sos.epdg.epc.mnc"); 767 } else { 768 domainName.append(".tac.epdg.epc.mnc"); 769 } 770 domainName 771 .append(mccmnc[1]) 772 .append(".mcc") 773 .append(mccmnc[0]) 774 .append(".pub.3gppnetwork.org"); 775 getIP(domainName.toString(), filter, validIpList, network); 776 domainName.setLength(0); 777 } 778 } else if (cellInfo instanceof CellInfoNr) { 779 CellIdentityNr nrCellId = (CellIdentityNr) cellInfo.getCellIdentity(); 780 String tacString = String.format("%06x", nrCellId.getTac()); 781 String[] tacSubString = new String[3]; 782 tacSubString[0] = tacString.substring(0, 2); 783 tacSubString[1] = tacString.substring(2, 4); 784 tacSubString[2] = tacString.substring(4); 785 786 plmnList = getPlmnList(); 787 for (String plmn : plmnList) { 788 String[] mccmnc = splitMccMnc(plmn); 789 /** 790 * 5GS Tracking Area Identity based ePDG FQDN format: 791 * tac-lb<TAC-low-byte>.tac-mb<TAC-middle-byte>.tac-hb<TAC-high-byte>. 792 * 5gstac.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 793 * 794 * <p>5GS Tracking Area Identity based Emergency ePDG FQDN format: 795 * tac-lb<TAC-low-byte>.tac-mb<TAC-middle-byte>.tac-hb<TAC-high-byte>. 796 * 5gstac.sos.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 797 */ 798 domainName 799 .append("tac-lb") 800 .append(tacSubString[2]) 801 .append(".tac-mb") 802 .append(tacSubString[1]) 803 .append(".tac-hb") 804 .append(tacSubString[0]); 805 if (isEmergency) { 806 domainName.append(".5gstac.sos.epdg.epc.mnc"); 807 } else { 808 domainName.append(".5gstac.epdg.epc.mnc"); 809 } 810 domainName 811 .append(mccmnc[1]) 812 .append(".mcc") 813 .append(mccmnc[0]) 814 .append(".pub.3gppnetwork.org"); 815 getIP(domainName.toString(), filter, validIpList, network); 816 domainName.setLength(0); 817 } 818 } else { 819 Log.d(TAG, "This cell doesn't contain LAC/TAC info"); 820 } 821 } 822 } 823 lacDomainNameResolution( int filter, List<InetAddress> validIpList, String lacString, boolean isEmergency, Network network)824 private void lacDomainNameResolution( 825 int filter, 826 List<InetAddress> validIpList, 827 String lacString, 828 boolean isEmergency, 829 Network network) { 830 String[] plmnList; 831 StringBuilder domainName = new StringBuilder(); 832 833 plmnList = getPlmnList(); 834 for (String plmn : plmnList) { 835 String[] mccmnc = splitMccMnc(plmn); 836 /** 837 * Location Area Identity based ePDG FQDN format: 838 * lac<LAC>.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 839 * 840 * <p>Location Area Identity based Emergency ePDG FQDN format: 841 * lac<LAC>.sos.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 842 */ 843 domainName.append("lac").append(lacString); 844 if (isEmergency) { 845 domainName.append(".sos.epdg.epc.mnc"); 846 } else { 847 domainName.append(".epdg.epc.mnc"); 848 } 849 domainName 850 .append(mccmnc[1]) 851 .append(".mcc") 852 .append(mccmnc[0]) 853 .append(".pub.3gppnetwork.org"); 854 855 getIP(domainName.toString(), filter, validIpList, network); 856 domainName.setLength(0); 857 } 858 } 859 resolutionMethodPco(int filter, List<InetAddress> validIpList)860 private void resolutionMethodPco(int filter, List<InetAddress> validIpList) { 861 Log.d(TAG, "PCO Method"); 862 863 int PCO_ID_IPV6 = 864 IwlanHelper.getConfig( 865 CarrierConfigManager.Iwlan.KEY_EPDG_PCO_ID_IPV6_INT, mContext, mSlotId); 866 int PCO_ID_IPV4 = 867 IwlanHelper.getConfig( 868 CarrierConfigManager.Iwlan.KEY_EPDG_PCO_ID_IPV4_INT, mContext, mSlotId); 869 870 switch (filter) { 871 case PROTO_FILTER_IPV4: 872 if (mV4PcoId != PCO_ID_IPV4) { 873 clearPcoData(); 874 } else { 875 getInetAddressWithPcoData(mV4PcoData, validIpList); 876 } 877 break; 878 case PROTO_FILTER_IPV6: 879 if (mV6PcoId != PCO_ID_IPV6) { 880 clearPcoData(); 881 } else { 882 getInetAddressWithPcoData(mV6PcoData, validIpList); 883 } 884 break; 885 case PROTO_FILTER_IPV4V6: 886 if ((mV4PcoId != PCO_ID_IPV4) || (mV6PcoId != PCO_ID_IPV6)) { 887 clearPcoData(); 888 } else { 889 getInetAddressWithPcoData(mV4PcoData, validIpList); 890 getInetAddressWithPcoData(mV6PcoData, validIpList); 891 } 892 break; 893 default: 894 Log.d(TAG, "Invalid ProtoFilter : " + filter); 895 } 896 } 897 getInetAddressWithPcoData(byte[] pcoData, List<InetAddress> validIpList)898 private void getInetAddressWithPcoData(byte[] pcoData, List<InetAddress> validIpList) { 899 InetAddress ipAddress; 900 if (pcoData != null && pcoData.length > 0) { 901 try { 902 ipAddress = InetAddress.getByAddress(pcoData); 903 validIpList.add(ipAddress); 904 } catch (Exception e) { 905 Log.e(TAG, "Exception when querying IP address : " + e); 906 } 907 } else { 908 Log.d(TAG, "Empty PCO data"); 909 } 910 } 911 composeFqdnWithMccMnc(String mcc, String mnc, boolean isEmergency)912 private String composeFqdnWithMccMnc(String mcc, String mnc, boolean isEmergency) { 913 StringBuilder domainName = new StringBuilder(); 914 915 /* 916 * Operator Identifier based ePDG FQDN format: 917 * epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 918 * 919 * Operator Identifier based Emergency ePDG FQDN format: 920 * sos.epdg.epc.mnc<MNC>.mcc<MCC>.pub.3gppnetwork.org 921 */ 922 domainName.setLength(0); 923 if (isEmergency) { 924 domainName.append("sos."); 925 } 926 domainName 927 .append("epdg.epc.mnc") 928 .append(mnc) 929 .append(".mcc") 930 .append(mcc) 931 .append(".pub.3gppnetwork.org"); 932 933 return domainName.toString(); 934 } 935 isRegisteredWith3GPP(TelephonyManager telephonyManager)936 private boolean isRegisteredWith3GPP(TelephonyManager telephonyManager) { 937 List<CellInfo> cellInfoList = telephonyManager.getAllCellInfo(); 938 if (cellInfoList == null) { 939 Log.e(TAG, "cellInfoList is NULL"); 940 } else { 941 for (CellInfo cellInfo : cellInfoList) { 942 if (!cellInfo.isRegistered()) { 943 continue; 944 } 945 if (cellInfo instanceof CellInfoGsm 946 || cellInfo instanceof CellInfoTdscdma 947 || cellInfo instanceof CellInfoWcdma 948 || cellInfo instanceof CellInfoLte 949 || cellInfo instanceof CellInfoNr) { 950 return true; 951 } 952 } 953 } 954 return false; 955 } 956 processNaptrResponse( int filter, List<InetAddress> validIpList, boolean isEmergency, Network network, boolean isRegisteredWith3GPP, List<NaptrTarget> naptrResponse, Set<String> plmnsFromCarrierConfig, String registeredhostName)957 private void processNaptrResponse( 958 int filter, 959 List<InetAddress> validIpList, 960 boolean isEmergency, 961 Network network, 962 boolean isRegisteredWith3GPP, 963 List<NaptrTarget> naptrResponse, 964 Set<String> plmnsFromCarrierConfig, 965 String registeredhostName) { 966 Set<String> resultSet = new LinkedHashSet<>(); 967 968 for (NaptrTarget target : naptrResponse) { 969 Log.d(TAG, "NaptrTarget - name: " + target.mName); 970 Log.d(TAG, "NaptrTarget - type: " + target.mType); 971 if (target.mType == NaptrDnsResolver.TYPE_A) { 972 resultSet.add(target.mName); 973 } 974 } 975 976 /* 977 * As 3GPP TS 23.402 4.5.4.5 bullet 2a, 978 * if the device registers via 3GPP and its PLMN info is in the NAPTR response, 979 * try to connect ePDG with this PLMN info. 980 */ 981 if (isRegisteredWith3GPP) { 982 if (resultSet.contains(registeredhostName)) { 983 getIP(registeredhostName, filter, validIpList, network); 984 resultSet.remove(registeredhostName); 985 } 986 } 987 988 /* 989 * As 3GPP TS 23.402 4.5.4.5 bullet 2b 990 * Check if there is any PLMN in both ePDG selection information and the DNS response 991 */ 992 for (String plmn : plmnsFromCarrierConfig) { 993 String[] mccmnc = splitMccMnc(plmn); 994 String carrierConfighostName = composeFqdnWithMccMnc(mccmnc[0], mccmnc[1], isEmergency); 995 996 if (resultSet.contains(carrierConfighostName)) { 997 getIP(carrierConfighostName, filter, validIpList, network); 998 resultSet.remove(carrierConfighostName); 999 } 1000 } 1001 1002 /* 1003 * Do FQDN with the remaining PLMNs in the ResultSet 1004 */ 1005 for (String result : resultSet) { 1006 getIP(result, filter, validIpList, network); 1007 } 1008 } 1009 resolutionMethodVisitedCountry( int filter, List<InetAddress> validIpList, boolean isEmergency, Network network)1010 private void resolutionMethodVisitedCountry( 1011 int filter, List<InetAddress> validIpList, boolean isEmergency, Network network) { 1012 StringBuilder domainName = new StringBuilder(); 1013 1014 TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); 1015 telephonyManager = 1016 Objects.requireNonNull(telephonyManager) 1017 .createForSubscriptionId(IwlanHelper.getSubId(mContext, mSlotId)); 1018 1019 if (telephonyManager == null) { 1020 Log.e(TAG, "TelephonyManager is NULL"); 1021 return; 1022 } 1023 1024 final boolean isRegisteredWith3GPP = isRegisteredWith3GPP(telephonyManager); 1025 1026 // Get ePDG selection information from CarrierConfig 1027 final Set<String> plmnsFromCarrierConfig = 1028 new LinkedHashSet<>( 1029 Arrays.asList( 1030 IwlanHelper.getConfig( 1031 CarrierConfigManager.Iwlan.KEY_MCC_MNCS_STRING_ARRAY, 1032 mContext, 1033 mSlotId))); 1034 1035 final String cellMcc = telephonyManager.getNetworkOperator().substring(0, 3); 1036 final String cellMnc = telephonyManager.getNetworkOperator().substring(3); 1037 final String plmnFromNetwork = cellMcc + "-" + cellMnc; 1038 final String registeredhostName = composeFqdnWithMccMnc(cellMcc, cellMnc, isEmergency); 1039 1040 /* 1041 * As TS 23 402 4.5.4.4 bullet 3a 1042 * If the UE determines to be located in a country other than its home country 1043 * If the UE is registered via 3GPP access to a PLMN and this PLMN matches an entry 1044 in the ePDG selection information, then the UE shall select an ePDG in this PLMN. 1045 */ 1046 if (isRegisteredWith3GPP) { 1047 if (plmnsFromCarrierConfig.contains(plmnFromNetwork)) { 1048 getIP(registeredhostName, filter, validIpList, network); 1049 } 1050 } 1051 1052 /* 1053 * Visited Country FQDN format: 1054 * epdg.epc.mcc<MCC>.visited-country.pub.3gppnetwork.org 1055 * 1056 * Visited Country Emergency ePDG FQDN format: 1057 * sos.epdg.epc.mcc<MCC>.visited-country.pub.3gppnetwork.org 1058 */ 1059 if (isEmergency) { 1060 domainName.append("sos."); 1061 } 1062 domainName 1063 .append("epdg.epc.mcc") 1064 .append(cellMcc) 1065 .append(".visited-country.pub.3gppnetwork.org"); 1066 1067 Log.d(TAG, "Visited Country FQDN with " + domainName); 1068 1069 CompletableFuture<List<NaptrTarget>> naptrDnsResult = new CompletableFuture<>(); 1070 DnsResolver.Callback<List<NaptrTarget>> naptrDnsCb = 1071 new DnsResolver.Callback<List<NaptrTarget>>() { 1072 @Override 1073 public void onAnswer(@NonNull final List<NaptrTarget> answer, final int rcode) { 1074 if (rcode == 0 && answer.size() != 0) { 1075 naptrDnsResult.complete(answer); 1076 } else { 1077 naptrDnsResult.completeExceptionally(new UnknownHostException()); 1078 } 1079 } 1080 1081 @Override 1082 public void onError(@Nullable final DnsException error) { 1083 naptrDnsResult.completeExceptionally(error); 1084 } 1085 }; 1086 NaptrDnsResolver.query(network, domainName.toString(), Runnable::run, null, naptrDnsCb); 1087 1088 try { 1089 final List<NaptrTarget> naptrResponse = 1090 naptrDnsResult.get(DNS_RESOLVER_TIMEOUT_DURATION_SEC, TimeUnit.SECONDS); 1091 // Check if there is any record in the NAPTR response 1092 if (naptrResponse != null && naptrResponse.size() > 0) { 1093 processNaptrResponse( 1094 filter, 1095 validIpList, 1096 isEmergency, 1097 network, 1098 isRegisteredWith3GPP, 1099 naptrResponse, 1100 plmnsFromCarrierConfig, 1101 registeredhostName); 1102 } 1103 } catch (ExecutionException e) { 1104 Log.e(TAG, "Cause of ExecutionException: ", e.getCause()); 1105 } catch (InterruptedException e) { 1106 Thread thread = Thread.currentThread(); 1107 if (thread.interrupted()) { 1108 thread.interrupt(); 1109 } 1110 Log.e(TAG, "InterruptedException: ", e); 1111 } catch (TimeoutException e) { 1112 Log.e(TAG, "TimeoutException: ", e); 1113 } 1114 } 1115 1116 // Cancels duplicate prefetches a prefetch is already running. Always schedules tunnel bringup. trySubmitEpdgSelectionExecutor( Runnable runnable, boolean isPrefetch, boolean isEmergency)1117 private void trySubmitEpdgSelectionExecutor( 1118 Runnable runnable, boolean isPrefetch, boolean isEmergency) { 1119 if (isEmergency) { 1120 if (isPrefetch) { 1121 if (mSosDnsPrefetchFuture == null || mSosDnsPrefetchFuture.isDone()) { 1122 mSosDnsPrefetchFuture = mSosEpdgSelectionExecutor.submit(runnable); 1123 } 1124 } else { 1125 mSosEpdgSelectionExecutor.execute(runnable); 1126 } 1127 } else { 1128 if (isPrefetch) { 1129 if (mDnsPrefetchFuture == null || mDnsPrefetchFuture.isDone()) { 1130 mDnsPrefetchFuture = mEpdgSelectionExecutor.submit(runnable); 1131 } 1132 } else { 1133 mEpdgSelectionExecutor.execute(runnable); 1134 } 1135 } 1136 } 1137 1138 /** 1139 * Asynchronously runs DNS resolution on a carrier-specific list of ePDG servers into IP 1140 * addresses, and passes them to the caller via the {@link EpdgSelectorCallback}. 1141 * 1142 * @param transactionId A unique ID passed in to match the response with the request. If this 1143 * value is 0, the caller is not interested in the result. 1144 * @param filter Allows the caller to filter for IPv4 or IPv6 servers, or both. 1145 * @param isRoaming Specifies whether the subscription is currently in roaming state. 1146 * @param isEmergency Specifies whether the ePDG server lookup is to make an emergency call. 1147 * @param network {@link Network} The server lookups will be performed over this Network. 1148 * @param selectorCallback {@link EpdgSelectorCallback} The result will be returned through this 1149 * callback. If null, the caller is not interested in the result. Typically, this means the 1150 * caller is performing DNS prefetch of the ePDG server addresses to warm the native 1151 * dnsresolver module's caches. 1152 * @return {link IwlanError} denoting the status of this operation. 1153 */ getValidatedServerList( int transactionId, @ProtoFilter int filter, @EpdgAddressOrder int order, boolean isRoaming, boolean isEmergency, @NonNull Network network, EpdgSelectorCallback selectorCallback)1154 public IwlanError getValidatedServerList( 1155 int transactionId, 1156 @ProtoFilter int filter, 1157 @EpdgAddressOrder int order, 1158 boolean isRoaming, 1159 boolean isEmergency, 1160 @NonNull Network network, 1161 EpdgSelectorCallback selectorCallback) { 1162 1163 final Runnable epdgSelectionRunnable = 1164 () -> { 1165 List<InetAddress> validIpList = new ArrayList<>(); 1166 Log.d( 1167 TAG, 1168 "Processing request with transactionId: " 1169 + transactionId 1170 + ", for slotID: " 1171 + mSlotId 1172 + ", isEmergency: " 1173 + isEmergency); 1174 1175 int[] addrResolutionMethods = 1176 IwlanHelper.getConfig( 1177 CarrierConfigManager.Iwlan.KEY_EPDG_ADDRESS_PRIORITY_INT_ARRAY, 1178 mContext, 1179 mSlotId); 1180 1181 final boolean isVisitedCountryMethodRequired = 1182 Arrays.stream(addrResolutionMethods) 1183 .anyMatch( 1184 i -> 1185 i 1186 == CarrierConfigManager.Iwlan 1187 .EPDG_ADDRESS_VISITED_COUNTRY); 1188 1189 // In the visited country 1190 if (isRoaming && !inSameCountry() && isVisitedCountryMethodRequired) { 1191 resolutionMethodVisitedCountry(filter, validIpList, isEmergency, network); 1192 } 1193 1194 Map<String, List<InetAddress>> plmnDomainNamesToIpAddress = null; 1195 for (int addrResolutionMethod : addrResolutionMethods) { 1196 switch (addrResolutionMethod) { 1197 case CarrierConfigManager.Iwlan.EPDG_ADDRESS_STATIC: 1198 resolutionMethodStatic(filter, validIpList, network); 1199 break; 1200 1201 case CarrierConfigManager.Iwlan.EPDG_ADDRESS_PLMN: 1202 plmnDomainNamesToIpAddress = 1203 resolutionMethodPlmn( 1204 filter, validIpList, isEmergency, network); 1205 break; 1206 1207 case CarrierConfigManager.Iwlan.EPDG_ADDRESS_PCO: 1208 resolutionMethodPco(filter, validIpList); 1209 break; 1210 1211 case CarrierConfigManager.Iwlan.EPDG_ADDRESS_CELLULAR_LOC: 1212 resolutionMethodCellularLoc( 1213 filter, validIpList, isEmergency, network); 1214 break; 1215 1216 default: 1217 Log.d( 1218 TAG, 1219 "Incorrect address resolution method " 1220 + addrResolutionMethod); 1221 } 1222 } 1223 1224 if (selectorCallback != null) { 1225 if (mErrorPolicyManager.getMostRecentDataFailCause() 1226 == DataFailCause.IWLAN_CONGESTION) { 1227 Objects.requireNonNull(plmnDomainNamesToIpAddress) 1228 .values() 1229 .removeIf(List::isEmpty); 1230 1231 int numFqdns = plmnDomainNamesToIpAddress.size(); 1232 int index = mErrorPolicyManager.getCurrentFqdnIndex(numFqdns); 1233 if (index >= 0 && index < numFqdns) { 1234 Object[] keys = plmnDomainNamesToIpAddress.keySet().toArray(); 1235 validIpList = plmnDomainNamesToIpAddress.get((String) keys[index]); 1236 } else { 1237 Log.w( 1238 TAG, 1239 "CONGESTION error handling- invalid index: " 1240 + index 1241 + " number of PLMN FQDNs: " 1242 + numFqdns); 1243 } 1244 } 1245 1246 if (!validIpList.isEmpty()) { 1247 prioritizeIp(validIpList, order); 1248 selectorCallback.onServerListChanged( 1249 transactionId, removeDuplicateIp(validIpList)); 1250 } else { 1251 selectorCallback.onError( 1252 transactionId, 1253 new IwlanError( 1254 IwlanError.EPDG_SELECTOR_SERVER_SELECTION_FAILED)); 1255 } 1256 } 1257 }; 1258 1259 boolean isPrefetch = (selectorCallback == null); 1260 trySubmitEpdgSelectionExecutor(epdgSelectionRunnable, isPrefetch, isEmergency); 1261 1262 return new IwlanError(IwlanError.NO_ERROR); 1263 } 1264 1265 /** 1266 * Validates a PLMN (Public Land Mobile Network) identifier string. 1267 * 1268 * @param plmn The PLMN identifier string to validate. 1269 * @return True if the PLMN identifier is valid, false otherwise. 1270 */ isValidPlmn(String plmn)1271 private static boolean isValidPlmn(String plmn) { 1272 return plmn != null && plmn.matches("\\d{5,6}"); 1273 } 1274 } 1275