1package eu.siacs.conversations.utils;
 2
 3import android.annotation.TargetApi;
 4import android.content.Context;
 5import android.net.ConnectivityManager;
 6import android.net.LinkProperties;
 7import android.net.Network;
 8import android.net.NetworkInfo;
 9import android.net.RouteInfo;
10import android.os.Build;
11
12import java.net.Inet4Address;
13import java.net.InetAddress;
14import java.util.ArrayList;
15import java.util.List;
16import java.util.stream.Collectors;
17
18import org.minidns.dnsserverlookup.AbstractDnsServerLookupMechanism;
19import org.minidns.dnsserverlookup.AndroidUsingExec;
20
21public class AndroidUsingLinkProperties extends AbstractDnsServerLookupMechanism {
22
23    private final Context context;
24
25    AndroidUsingLinkProperties(Context context) {
26        super(AndroidUsingLinkProperties.class.getSimpleName(), AndroidUsingExec.PRIORITY - 1);
27        this.context = context;
28    }
29
30    @Override
31    public boolean isAvailable() {
32        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
33    }
34
35    @Override
36    @TargetApi(21)
37    public List<String> getDnsServerAddresses() {
38        final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
39        final Network[] networks = connectivityManager == null ? null : connectivityManager.getAllNetworks();
40        if (networks == null) {
41            return new ArrayList<>();
42        }
43        final Network activeNetwork = getActiveNetwork(connectivityManager);
44        final List<String> networkServers = new ArrayList<>();
45        final List<String> otherServers = new ArrayList<>();
46        for(Network network : networks) {
47            LinkProperties linkProperties = connectivityManager.getLinkProperties(network);
48            if (linkProperties == null) {
49                continue;
50            }
51            final NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network);
52            final boolean isActiveNetwork = network.equals(activeNetwork);
53            final boolean isVpn = networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_VPN;
54            final List<String> servers = getIPv4First(linkProperties.getDnsServers());
55            if (hasDefaultRoute(linkProperties) || isActiveNetwork || activeNetwork == null || isVpn) {
56                if (isActiveNetwork) networkServers.addAll(0, servers);
57                if (isVpn) networkServers.addAll(servers);
58                otherServers.addAll(servers);
59            }
60        }
61        return (networkServers.isEmpty() ? otherServers : networkServers).stream().distinct().collect(Collectors.toList());
62    }
63
64    @TargetApi(23)
65    private static Network getActiveNetwork(ConnectivityManager cm) {
66        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? cm.getActiveNetwork() : null;
67    }
68
69    private static List<String> getIPv4First(List<InetAddress> in) {
70        List<String> out = new ArrayList<>();
71        for(InetAddress address : in) {
72            if (address instanceof Inet4Address) {
73                out.add(0, address.getHostAddress());
74            } else {
75                out.add(address.getHostAddress());
76            }
77        }
78        return out;
79    }
80
81    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
82    private static boolean hasDefaultRoute(LinkProperties linkProperties) {
83        for(RouteInfo route: linkProperties.getRoutes()) {
84            if (route.isDefaultRoute()) {
85                return true;
86            }
87        }
88        return false;
89    }
90}