1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.connectivity; 18 19 import static android.net.ConnectivitySettingsManager.DNS_RESOLVER_MAX_SAMPLES; 20 import static android.net.ConnectivitySettingsManager.DNS_RESOLVER_MIN_SAMPLES; 21 import static android.net.ConnectivitySettingsManager.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS; 22 import static android.net.ConnectivitySettingsManager.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT; 23 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_DEFAULT_MODE; 24 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE; 25 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF; 26 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; 27 import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_SPECIFIER; 28 import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE; 29 import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_SUCCESS; 30 31 import android.annotation.NonNull; 32 import android.annotation.Nullable; 33 import android.content.ContentResolver; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.net.ConnectivityManager; 37 import android.net.ConnectivitySettingsManager; 38 import android.net.IDnsResolver; 39 import android.net.InetAddresses; 40 import android.net.LinkProperties; 41 import android.net.Network; 42 import android.net.NetworkCapabilities; 43 import android.net.ResolverParamsParcel; 44 import android.net.Uri; 45 import android.net.resolv.aidl.DohParamsParcel; 46 import android.net.shared.PrivateDnsConfig; 47 import android.os.Binder; 48 import android.os.RemoteException; 49 import android.os.ServiceSpecificException; 50 import android.os.UserHandle; 51 import android.provider.Settings; 52 import android.text.TextUtils; 53 import android.util.Log; 54 import android.util.Pair; 55 56 import java.net.InetAddress; 57 import java.util.ArrayList; 58 import java.util.Arrays; 59 import java.util.Collection; 60 import java.util.Collections; 61 import java.util.HashMap; 62 import java.util.HashSet; 63 import java.util.Iterator; 64 import java.util.List; 65 import java.util.Map; 66 import java.util.Set; 67 import java.util.concurrent.ConcurrentHashMap; 68 69 /** 70 * Encapsulate the management of DNS settings for networks. 71 * 72 * This class it NOT designed for concurrent access. Furthermore, all non-static 73 * methods MUST be called from ConnectivityService's thread. However, an exceptional 74 * case is getPrivateDnsConfig(Network) which is exclusively for 75 * ConnectivityService#dumpNetworkDiagnostics() on a random binder thread. 76 * 77 * [ Private DNS ] 78 * The code handling Private DNS is spread across several components, but this 79 * seems like the least bad place to collect all the observations. 80 * 81 * Private DNS handling and updating occurs in response to several different 82 * events. Each is described here with its corresponding intended handling. 83 * 84 * [A] Event: A new network comes up. 85 * Mechanics: 86 * [1] ConnectivityService gets notifications from NetworkAgents. 87 * [2] in updateNetworkInfo(), the first time the NetworkAgent goes into 88 * into CONNECTED state, the Private DNS configuration is retrieved, 89 * programmed, and strict mode hostname resolution (if applicable) is 90 * enqueued in NetworkAgent's NetworkMonitor, via a call to 91 * handlePerNetworkPrivateDnsConfig(). 92 * [3] Re-resolution of strict mode hostnames that fail to return any 93 * IP addresses happens inside NetworkMonitor; it sends itself a 94 * delayed CMD_EVALUATE_PRIVATE_DNS message in a simple backoff 95 * schedule. 96 * [4] Successfully resolved hostnames are sent to ConnectivityService 97 * inside an EVENT_PRIVATE_DNS_CONFIG_RESOLVED message. The resolved 98 * IP addresses are programmed into netd via: 99 * 100 * updatePrivateDns() -> updateDnses() 101 * 102 * both of which make calls into DnsManager. 103 * [5] Upon a successful hostname resolution NetworkMonitor initiates a 104 * validation attempt in the form of a lookup for a one-time hostname 105 * that uses Private DNS. 106 * 107 * [B] Event: Private DNS settings are changed. 108 * Mechanics: 109 * [1] ConnectivityService gets notifications from its SettingsObserver. 110 * [2] handlePrivateDnsSettingsChanged() is called, which calls 111 * handlePerNetworkPrivateDnsConfig() and the process proceeds 112 * as if from A.3 above. 113 * 114 * [C] Event: An application calls ConnectivityManager#reportBadNetwork(). 115 * Mechanics: 116 * [1] NetworkMonitor is notified and initiates a reevaluation, which 117 * always bypasses Private DNS. 118 * [2] Once completed, NetworkMonitor checks if strict mode is in operation 119 * and if so enqueues another evaluation of Private DNS, as if from 120 * step A.5 above. 121 * 122 * @hide 123 */ 124 public class DnsManager { 125 private static final String TAG = DnsManager.class.getSimpleName(); 126 private static final PrivateDnsConfig PRIVATE_DNS_OFF = new PrivateDnsConfig(); 127 128 /* Defaults for resolver parameters. */ 129 private static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800; 130 private static final int DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT = 25; 131 private static final int DNS_RESOLVER_DEFAULT_MIN_SAMPLES = 8; 132 private static final int DNS_RESOLVER_DEFAULT_MAX_SAMPLES = 64; 133 134 /** 135 * Get PrivateDnsConfig. 136 */ getPrivateDnsConfig(Context context)137 public static PrivateDnsConfig getPrivateDnsConfig(Context context) { 138 final int mode = ConnectivitySettingsManager.getPrivateDnsMode(context); 139 140 final boolean useTls = mode != PRIVATE_DNS_MODE_OFF; 141 142 if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME == mode) { 143 final String specifier = getStringSetting(context.getContentResolver(), 144 PRIVATE_DNS_SPECIFIER); 145 return new PrivateDnsConfig(specifier, null); 146 } 147 148 return new PrivateDnsConfig(useTls); 149 } 150 getPrivateDnsSettingsUris()151 public static Uri[] getPrivateDnsSettingsUris() { 152 return new Uri[]{ 153 Settings.Global.getUriFor(PRIVATE_DNS_DEFAULT_MODE), 154 Settings.Global.getUriFor(PRIVATE_DNS_MODE), 155 Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER), 156 }; 157 } 158 159 public static class PrivateDnsValidationUpdate { 160 public final int netId; 161 public final InetAddress ipAddress; 162 public final String hostname; 163 // Refer to IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_*. 164 public final int validationResult; 165 PrivateDnsValidationUpdate(int netId, InetAddress ipAddress, String hostname, int validationResult)166 public PrivateDnsValidationUpdate(int netId, InetAddress ipAddress, 167 String hostname, int validationResult) { 168 this.netId = netId; 169 this.ipAddress = ipAddress; 170 this.hostname = hostname; 171 this.validationResult = validationResult; 172 } 173 } 174 175 private static class PrivateDnsValidationStatuses { 176 enum ValidationStatus { 177 IN_PROGRESS, 178 FAILED, 179 SUCCEEDED 180 } 181 182 // Validation statuses of <hostname, ipAddress> pairs for a single netId 183 // Caution : not thread-safe. As mentioned in the top file comment, all 184 // methods of this class must only be called on ConnectivityService's thread. 185 private Map<Pair<String, InetAddress>, ValidationStatus> mValidationMap; 186 PrivateDnsValidationStatuses()187 private PrivateDnsValidationStatuses() { 188 mValidationMap = new HashMap<>(); 189 } 190 hasValidatedServer()191 private boolean hasValidatedServer() { 192 for (ValidationStatus status : mValidationMap.values()) { 193 if (status == ValidationStatus.SUCCEEDED) { 194 return true; 195 } 196 } 197 return false; 198 } 199 updateTrackedDnses(String[] ipAddresses, String hostname)200 private void updateTrackedDnses(String[] ipAddresses, String hostname) { 201 Set<Pair<String, InetAddress>> latestDnses = new HashSet<>(); 202 for (String ipAddress : ipAddresses) { 203 try { 204 latestDnses.add(new Pair(hostname, 205 InetAddresses.parseNumericAddress(ipAddress))); 206 } catch (IllegalArgumentException e) {} 207 } 208 // Remove <hostname, ipAddress> pairs that should not be tracked. 209 for (Iterator<Map.Entry<Pair<String, InetAddress>, ValidationStatus>> it = 210 mValidationMap.entrySet().iterator(); it.hasNext(); ) { 211 Map.Entry<Pair<String, InetAddress>, ValidationStatus> entry = it.next(); 212 if (!latestDnses.contains(entry.getKey())) { 213 it.remove(); 214 } 215 } 216 // Add new <hostname, ipAddress> pairs that should be tracked. 217 for (Pair<String, InetAddress> p : latestDnses) { 218 if (!mValidationMap.containsKey(p)) { 219 mValidationMap.put(p, ValidationStatus.IN_PROGRESS); 220 } 221 } 222 } 223 updateStatus(PrivateDnsValidationUpdate update)224 private void updateStatus(PrivateDnsValidationUpdate update) { 225 Pair<String, InetAddress> p = new Pair(update.hostname, 226 update.ipAddress); 227 if (!mValidationMap.containsKey(p)) { 228 return; 229 } 230 if (update.validationResult == VALIDATION_RESULT_SUCCESS) { 231 mValidationMap.put(p, ValidationStatus.SUCCEEDED); 232 } else if (update.validationResult == VALIDATION_RESULT_FAILURE) { 233 mValidationMap.put(p, ValidationStatus.FAILED); 234 } else { 235 Log.e(TAG, "Unknown private dns validation operation=" 236 + update.validationResult); 237 } 238 } 239 fillInValidatedPrivateDns(LinkProperties lp)240 private LinkProperties fillInValidatedPrivateDns(LinkProperties lp) { 241 lp.setValidatedPrivateDnsServers(Collections.EMPTY_LIST); 242 mValidationMap.forEach((key, value) -> { 243 if (value == ValidationStatus.SUCCEEDED) { 244 lp.addValidatedPrivateDnsServer(key.second); 245 } 246 }); 247 return lp; 248 } 249 } 250 251 private final Context mContext; 252 private final ContentResolver mContentResolver; 253 private final IDnsResolver mDnsResolver; 254 private final ConcurrentHashMap<Integer, PrivateDnsConfig> mPrivateDnsMap; 255 // TODO: Replace the Map with SparseArrays. 256 private final Map<Integer, PrivateDnsValidationStatuses> mPrivateDnsValidationMap; 257 private final Map<Integer, LinkProperties> mLinkPropertiesMap; 258 private final Map<Integer, NetworkCapabilities> mNetworkCapabilitiesMap; 259 260 private int mSampleValidity; 261 private int mSuccessThreshold; 262 private int mMinSamples; 263 private int mMaxSamples; 264 DnsManager(Context ctx, IDnsResolver dnsResolver)265 public DnsManager(Context ctx, IDnsResolver dnsResolver) { 266 mContext = ctx; 267 mContentResolver = mContext.getContentResolver(); 268 mDnsResolver = dnsResolver; 269 mPrivateDnsMap = new ConcurrentHashMap<>(); 270 mPrivateDnsValidationMap = new HashMap<>(); 271 mLinkPropertiesMap = new HashMap<>(); 272 mNetworkCapabilitiesMap = new HashMap<>(); 273 274 // TODO: Create and register ContentObservers to track every setting 275 // used herein, posting messages to respond to changes. 276 } 277 getPrivateDnsConfig()278 public PrivateDnsConfig getPrivateDnsConfig() { 279 return getPrivateDnsConfig(mContext); 280 } 281 removeNetwork(Network network)282 public void removeNetwork(Network network) { 283 mPrivateDnsMap.remove(network.getNetId()); 284 mPrivateDnsValidationMap.remove(network.getNetId()); 285 mNetworkCapabilitiesMap.remove(network.getNetId()); 286 mLinkPropertiesMap.remove(network.getNetId()); 287 } 288 289 // This is exclusively called by ConnectivityService#dumpNetworkDiagnostics() which 290 // is not on the ConnectivityService handler thread. getPrivateDnsConfig(@onNull Network network)291 public PrivateDnsConfig getPrivateDnsConfig(@NonNull Network network) { 292 return mPrivateDnsMap.getOrDefault(network.getNetId(), PRIVATE_DNS_OFF); 293 } 294 updatePrivateDns(Network network, PrivateDnsConfig cfg)295 public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) { 296 Log.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")"); 297 return (cfg != null) 298 ? mPrivateDnsMap.put(network.getNetId(), cfg) 299 : mPrivateDnsMap.remove(network.getNetId()); 300 } 301 updatePrivateDnsStatus(int netId, LinkProperties lp)302 public void updatePrivateDnsStatus(int netId, LinkProperties lp) { 303 // Use the PrivateDnsConfig data pushed to this class instance 304 // from ConnectivityService. 305 final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId, 306 PRIVATE_DNS_OFF); 307 308 final boolean useTls = privateDnsCfg.mode != PRIVATE_DNS_MODE_OFF; 309 final PrivateDnsValidationStatuses statuses = 310 useTls ? mPrivateDnsValidationMap.get(netId) : null; 311 final boolean validated = (null != statuses) && statuses.hasValidatedServer(); 312 final boolean strictMode = privateDnsCfg.inStrictMode(); 313 final String tlsHostname = strictMode ? privateDnsCfg.hostname : null; 314 final boolean usingPrivateDns = strictMode || validated; 315 316 lp.setUsePrivateDns(usingPrivateDns); 317 lp.setPrivateDnsServerName(tlsHostname); 318 if (usingPrivateDns && null != statuses) { 319 statuses.fillInValidatedPrivateDns(lp); 320 } else { 321 lp.setValidatedPrivateDnsServers(Collections.EMPTY_LIST); 322 } 323 } 324 updatePrivateDnsValidation(PrivateDnsValidationUpdate update)325 public void updatePrivateDnsValidation(PrivateDnsValidationUpdate update) { 326 final PrivateDnsValidationStatuses statuses = mPrivateDnsValidationMap.get(update.netId); 327 if (statuses == null) return; 328 statuses.updateStatus(update); 329 } 330 331 /** 332 * Update {@link NetworkCapabilities} stored in this instance. 333 * 334 * In order to ensure that the resolver has access to necessary information when other events 335 * occur, capabilities are always saved to a hashMap before updating the DNS configuration 336 * whenever a new network is created, transport types are modified, or metered capabilities are 337 * altered for a network. When a network is destroyed, the corresponding entry is removed from 338 * the hashMap. To prevent concurrency issues, the hashMap should always be accessed from the 339 * same thread. 340 */ updateCapabilitiesForNetwork(int netId, @NonNull final NetworkCapabilities nc)341 public void updateCapabilitiesForNetwork(int netId, @NonNull final NetworkCapabilities nc) { 342 mNetworkCapabilitiesMap.put(netId, nc); 343 sendDnsConfigurationForNetwork(netId); 344 } 345 346 /** 347 * When {@link LinkProperties} are changed in a specific network, they are 348 * always saved to a hashMap before update dns config. 349 * When destroying network, the specific network will be removed from the hashMap. 350 * The hashMap is always accessed on the same thread. 351 */ noteDnsServersForNetwork(int netId, @NonNull LinkProperties lp)352 public void noteDnsServersForNetwork(int netId, @NonNull LinkProperties lp) { 353 mLinkPropertiesMap.put(netId, lp); 354 sendDnsConfigurationForNetwork(netId); 355 } 356 357 /** 358 * Send dns configuration parameters to resolver for a given network. 359 */ sendDnsConfigurationForNetwork(int netId)360 public void sendDnsConfigurationForNetwork(int netId) { 361 final LinkProperties lp = mLinkPropertiesMap.get(netId); 362 final NetworkCapabilities nc = mNetworkCapabilitiesMap.get(netId); 363 if (lp == null || nc == null) return; 364 updateParametersSettings(); 365 final ResolverParamsParcel paramsParcel = new ResolverParamsParcel(); 366 367 // We only use the PrivateDnsConfig data pushed to this class instance 368 // from ConnectivityService because it works in coordination with 369 // NetworkMonitor to decide which networks need validation and runs the 370 // blocking calls to resolve Private DNS strict mode hostnames. 371 // 372 // At this time we do not attempt to enable Private DNS on non-Internet 373 // networks like IMS. 374 final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId, 375 PRIVATE_DNS_OFF); 376 final boolean useTls = privateDnsCfg.mode != PRIVATE_DNS_MODE_OFF; 377 final boolean strictMode = privateDnsCfg.inStrictMode(); 378 379 paramsParcel.netId = netId; 380 paramsParcel.sampleValiditySeconds = mSampleValidity; 381 paramsParcel.successThreshold = mSuccessThreshold; 382 paramsParcel.minSamples = mMinSamples; 383 paramsParcel.maxSamples = mMaxSamples; 384 paramsParcel.servers = makeStrings(lp.getDnsServers()); 385 paramsParcel.domains = getDomainStrings(lp.getDomains()); 386 paramsParcel.tlsName = strictMode ? privateDnsCfg.hostname : ""; 387 paramsParcel.tlsServers = 388 strictMode ? makeStrings(getReachableAddressList(privateDnsCfg.ips, lp)) 389 : useTls ? paramsParcel.servers // Opportunistic 390 : new String[0]; // Off 391 paramsParcel.transportTypes = nc.getTransportTypes(); 392 paramsParcel.meteredNetwork = nc.isMetered(); 393 paramsParcel.interfaceNames = lp.getAllInterfaceNames().toArray(new String[0]); 394 paramsParcel.dohParams = makeDohParamsParcel(privateDnsCfg, lp); 395 396 // Prepare to track the validation status of the DNS servers in the 397 // resolver config when private DNS is in opportunistic or strict mode. 398 if (useTls) { 399 if (!mPrivateDnsValidationMap.containsKey(netId)) { 400 mPrivateDnsValidationMap.put(netId, new PrivateDnsValidationStatuses()); 401 } 402 mPrivateDnsValidationMap.get(netId).updateTrackedDnses(paramsParcel.tlsServers, 403 paramsParcel.tlsName); 404 } else { 405 mPrivateDnsValidationMap.remove(netId); 406 } 407 408 Log.d(TAG, "sendDnsConfigurationForNetwork(" + paramsParcel + ")"); 409 try { 410 mDnsResolver.setResolverConfiguration(paramsParcel); 411 } catch (RemoteException | ServiceSpecificException e) { 412 Log.e(TAG, "Error setting DNS configuration: " + e); 413 } 414 } 415 416 /** 417 * Flush DNS caches and events work before boot has completed. 418 */ flushVmDnsCache()419 public void flushVmDnsCache() { 420 /* 421 * Tell the VMs to toss their DNS caches 422 */ 423 final Intent intent = new Intent(ConnectivityManager.ACTION_CLEAR_DNS_CACHE); 424 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 425 /* 426 * Connectivity events can happen before boot has completed ... 427 */ 428 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 429 final long ident = Binder.clearCallingIdentity(); 430 try { 431 mContext.sendBroadcastAsUser(intent, UserHandle.ALL); 432 } finally { 433 Binder.restoreCallingIdentity(ident); 434 } 435 } 436 updateParametersSettings()437 private void updateParametersSettings() { 438 mSampleValidity = getIntSetting( 439 DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS, 440 DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS); 441 if (mSampleValidity < 0 || mSampleValidity > 65535) { 442 Log.w(TAG, "Invalid sampleValidity=" + mSampleValidity + ", using default=" 443 + DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS); 444 mSampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS; 445 } 446 447 mSuccessThreshold = getIntSetting( 448 DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT, 449 DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT); 450 if (mSuccessThreshold < 0 || mSuccessThreshold > 100) { 451 Log.w(TAG, "Invalid successThreshold=" + mSuccessThreshold + ", using default=" 452 + DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT); 453 mSuccessThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT; 454 } 455 456 mMinSamples = getIntSetting(DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES); 457 mMaxSamples = getIntSetting(DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES); 458 if (mMinSamples < 0 || mMinSamples > mMaxSamples || mMaxSamples > 64) { 459 Log.w(TAG, "Invalid sample count (min, max)=(" + mMinSamples + ", " + mMaxSamples 460 + "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " 461 + DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")"); 462 mMinSamples = DNS_RESOLVER_DEFAULT_MIN_SAMPLES; 463 mMaxSamples = DNS_RESOLVER_DEFAULT_MAX_SAMPLES; 464 } 465 } 466 getIntSetting(String which, int dflt)467 private int getIntSetting(String which, int dflt) { 468 return Settings.Global.getInt(mContentResolver, which, dflt); 469 } 470 471 /** 472 * Create a string array of host addresses from a collection of InetAddresses 473 * 474 * @param addrs a Collection of InetAddresses 475 * @return an array of Strings containing their host addresses 476 */ makeStrings(Collection<InetAddress> addrs)477 private String[] makeStrings(Collection<InetAddress> addrs) { 478 String[] result = new String[addrs.size()]; 479 int i = 0; 480 for (InetAddress addr : addrs) { 481 result[i++] = addr.getHostAddress(); 482 } 483 return result; 484 } 485 getStringSetting(ContentResolver cr, String which)486 private static String getStringSetting(ContentResolver cr, String which) { 487 return Settings.Global.getString(cr, which); 488 } 489 getDomainStrings(String domains)490 private static String[] getDomainStrings(String domains) { 491 return (TextUtils.isEmpty(domains)) ? new String[0] : domains.split(" "); 492 } 493 494 @NonNull getReachableAddressList(@onNull InetAddress[] ips, @NonNull LinkProperties lp)495 private List<InetAddress> getReachableAddressList(@NonNull InetAddress[] ips, 496 @NonNull LinkProperties lp) { 497 final ArrayList<InetAddress> out = new ArrayList<InetAddress>(Arrays.asList(ips)); 498 out.removeIf(ip -> !lp.isReachable(ip)); 499 return out; 500 } 501 502 @Nullable makeDohParamsParcel(@onNull PrivateDnsConfig cfg, @NonNull LinkProperties lp)503 private DohParamsParcel makeDohParamsParcel(@NonNull PrivateDnsConfig cfg, 504 @NonNull LinkProperties lp) { 505 if (!cfg.ddrEnabled) { 506 return null; 507 } 508 if (cfg.mode == PRIVATE_DNS_MODE_OFF) { 509 return new DohParamsParcel.Builder().build(); 510 } 511 return new DohParamsParcel.Builder() 512 .setName(cfg.dohName) 513 .setIps(makeStrings(getReachableAddressList(cfg.dohIps, lp))) 514 .setDohpath(cfg.dohPath) 515 .setPort(cfg.dohPort) 516 .build(); 517 } 518 } 519