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