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.net.InetAddress; 022import java.util.ArrayList; 023import java.util.List; 024 025import org.minidns.DnsClient; 026import org.minidns.dnsserverlookup.AbstractDnsServerLookupMechanism; 027import org.minidns.dnsserverlookup.AndroidUsingExec; 028 029/** 030 * A DNS server lookup mechanism using Android's Link Properties method available on Android API 21 or higher. Use 031 * {@link #setup(Context)} to setup this mechanism. 032 * <p> 033 * Requires the ACCESS_NETWORK_STATE permission. 034 * </p> 035 */ 036public class AndroidUsingLinkProperties extends AbstractDnsServerLookupMechanism { 037 038 private final ConnectivityManager connectivityManager; 039 040 /** 041 * Setup this DNS server lookup mechanism. You need to invoke this method only once, ideally before you do your 042 * first DNS lookup. 043 * 044 * @param context a Context instance. 045 * @return the instance of the newly setup mechanism 046 */ 047 public static AndroidUsingLinkProperties setup(Context context) { 048 AndroidUsingLinkProperties androidUsingLinkProperties = new AndroidUsingLinkProperties(context); 049 DnsClient.addDnsServerLookupMechanism(androidUsingLinkProperties); 050 return androidUsingLinkProperties; 051 } 052 053 public AndroidUsingLinkProperties(Context context) { 054 super(AndroidUsingLinkProperties.class.getSimpleName(), AndroidUsingExec.PRIORITY - 1); 055 connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 056 } 057 058 @Override 059 public boolean isAvailable() { 060 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; 061 } 062 063 @TargetApi(Build.VERSION_CODES.M) 064 private List<String> getDnsServerAddressesOfActiveNetwork() { 065 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 066 return null; 067 } 068 069 // ConnectivityManager.getActiveNetwork() is API 23. 070 Network activeNetwork = connectivityManager.getActiveNetwork(); 071 if (activeNetwork == null) { 072 return null; 073 } 074 075 LinkProperties linkProperties = connectivityManager.getLinkProperties(activeNetwork); 076 if (linkProperties == null) { 077 return null; 078 } 079 080 List<InetAddress> dnsServers = linkProperties.getDnsServers(); 081 return toListOfStrings(dnsServers); 082 } 083 084 @Override 085 @TargetApi(21) 086 public List<String> getDnsServerAddresses() { 087 // First, try the API 23 approach using ConnectivityManager.getActiveNetwork(). 088 List<String> servers = getDnsServerAddressesOfActiveNetwork(); 089 if (servers != null) { 090 return servers; 091 } 092 093 Network[] networks = connectivityManager.getAllNetworks(); 094 if (networks == null) { 095 return null; 096 } 097 098 servers = new ArrayList<>(networks.length * 2); 099 for (Network network : networks) { 100 LinkProperties linkProperties = connectivityManager.getLinkProperties(network); 101 if (linkProperties == null) { 102 continue; 103 } 104 105 // Prioritize the DNS servers of links which have a default route 106 if (hasDefaultRoute(linkProperties)) { 107 servers.addAll(0, toListOfStrings(linkProperties.getDnsServers())); 108 } else { 109 servers.addAll(toListOfStrings(linkProperties.getDnsServers())); 110 } 111 } 112 113 if (servers.isEmpty()) { 114 return null; 115 } 116 117 return servers; 118 } 119 120 @TargetApi(Build.VERSION_CODES.LOLLIPOP) 121 private static boolean hasDefaultRoute(LinkProperties linkProperties) { 122 for (RouteInfo route : linkProperties.getRoutes()) { 123 if (route.isDefaultRoute()) { 124 return true; 125 } 126 } 127 return false; 128 } 129 130}