001/*
002 * Copyright 2015-2020 the original author or authors
003 *
004 * This software is licensed under the Apache License, Version 2.0,
005 * the GNU Lesser General Public License version 2 or later ("LGPL")
006 * and the WTFPL.
007 * You may choose either license to govern your use of this software only
008 * upon the condition that you accept all of the terms of either
009 * the Apache License 2.0, the LGPL 2.1+ or the WTFPL.
010 */
011package org.minidns.dnsserverlookup.android21;
012
013import android.annotation.TargetApi;
014import android.content.Context;
015import android.net.ConnectivityManager;
016import android.net.LinkProperties;
017import android.net.Network;
018import android.net.RouteInfo;
019import android.os.Build;
020
021import java.util.ArrayList;
022import java.util.List;
023
024import org.minidns.DnsClient;
025import org.minidns.dnsserverlookup.AbstractDnsServerLookupMechanism;
026import org.minidns.dnsserverlookup.AndroidUsingExec;
027
028/**
029 * A DNS server lookup mechanism using Android's Link Properties method available on Android API 21 or higher. Use
030 * {@link #setup(Context)} to setup this mechanism.
031 * <p>
032 * Requires the ACCESS_NETWORK_STATE permission.
033 * </p>
034 */
035public class AndroidUsingLinkProperties extends AbstractDnsServerLookupMechanism {
036
037    private final ConnectivityManager connectivityManager;
038
039    /**
040     * Setup this DNS server lookup mechanism. You need to invoke this method only once, ideally before you do your
041     * first DNS lookup.
042     *
043     * @param context a Context instance.
044     * @return the instance of the newly setup mechanism
045     */
046    public static AndroidUsingLinkProperties setup(Context context) {
047        AndroidUsingLinkProperties androidUsingLinkProperties = new AndroidUsingLinkProperties(context);
048        DnsClient.addDnsServerLookupMechanism(androidUsingLinkProperties);
049        return androidUsingLinkProperties;
050    }
051
052    public AndroidUsingLinkProperties(Context context) {
053        super(AndroidUsingLinkProperties.class.getSimpleName(), AndroidUsingExec.PRIORITY - 1);
054        connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
055    }
056
057    @Override
058    public boolean isAvailable() {
059        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
060    }
061
062    @Override
063    @TargetApi(21)
064    public List<String> getDnsServerAddresses() {
065        Network[] networks = connectivityManager.getAllNetworks();
066        if (networks == null) {
067            return null;
068        }
069
070        List<String> servers = new ArrayList<>(networks.length * 2);
071        for (Network network : networks) {
072            LinkProperties linkProperties = connectivityManager.getLinkProperties(network);
073            if (linkProperties == null) {
074                continue;
075            }
076
077            // Prioritize the DNS servers of links which have a default route
078            if (hasDefaultRoute(linkProperties)) {
079                servers.addAll(0, toListOfStrings(linkProperties.getDnsServers()));
080            } else {
081                servers.addAll(toListOfStrings(linkProperties.getDnsServers()));
082            }
083        }
084
085        if (servers.isEmpty()) {
086            return null;
087        }
088
089        return servers;
090    }
091
092    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
093    private static boolean hasDefaultRoute(LinkProperties linkProperties) {
094        for (RouteInfo route : linkProperties.getRoutes()) {
095            if (route.isDefaultRoute()) {
096                return true;
097            }
098        }
099        return false;
100    }
101
102}