• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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