001/* 002 * Copyright 2015-2024 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 /** 054 * Construct this DNS server lookup mechanism. 055 * 056 * @param context an Android context. 057 */ 058 public AndroidUsingLinkProperties(Context context) { 059 super(AndroidUsingLinkProperties.class.getSimpleName(), AndroidUsingExec.PRIORITY - 1); 060 connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 061 } 062 063 @Override 064 public boolean isAvailable() { 065 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; 066 } 067 068 @TargetApi(Build.VERSION_CODES.M) 069 private List<String> getDnsServerAddressesOfActiveNetwork() { 070 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 071 return null; 072 } 073 074 // ConnectivityManager.getActiveNetwork() is API 23. 075 Network activeNetwork = connectivityManager.getActiveNetwork(); 076 if (activeNetwork == null) { 077 return null; 078 } 079 080 LinkProperties linkProperties = connectivityManager.getLinkProperties(activeNetwork); 081 if (linkProperties == null) { 082 return null; 083 } 084 085 List<InetAddress> dnsServers = linkProperties.getDnsServers(); 086 return toListOfStrings(dnsServers); 087 } 088 089 @Override 090 @TargetApi(21) 091 public List<String> getDnsServerAddresses() { 092 // First, try the API 23 approach using ConnectivityManager.getActiveNetwork(). 093 List<String> servers = getDnsServerAddressesOfActiveNetwork(); 094 if (servers != null) { 095 return servers; 096 } 097 098 Network[] networks = connectivityManager.getAllNetworks(); 099 if (networks == null) { 100 return null; 101 } 102 103 servers = new ArrayList<>(networks.length * 2); 104 for (Network network : networks) { 105 LinkProperties linkProperties = connectivityManager.getLinkProperties(network); 106 if (linkProperties == null) { 107 continue; 108 } 109 110 // Prioritize the DNS servers of links which have a default route 111 if (hasDefaultRoute(linkProperties)) { 112 servers.addAll(0, toListOfStrings(linkProperties.getDnsServers())); 113 } else { 114 servers.addAll(toListOfStrings(linkProperties.getDnsServers())); 115 } 116 } 117 118 if (servers.isEmpty()) { 119 return null; 120 } 121 122 return servers; 123 } 124 125 @TargetApi(Build.VERSION_CODES.LOLLIPOP) 126 private static boolean hasDefaultRoute(LinkProperties linkProperties) { 127 for (RouteInfo route : linkProperties.getRoutes()) { 128 if (route.isDefaultRoute()) { 129 return true; 130 } 131 } 132 return false; 133 } 134 135}