1. Android Q私人DNS功能初探在Android Q版本中谷歌引入了一项重要的隐私保护功能——私人DNSPrivate DNS。这个功能允许用户通过加密的DNS查询来保护自己的网络隐私防止第三方窃听或篡改DNS请求。简单来说就像给你的网络地址簿加了一把锁只有你和DNS服务器知道你要访问的真实网站地址。私人DNS提供了三种工作模式关闭模式使用运营商提供的传统DNS服务自动模式opportunistic尝试使用加密DNS失败时自动回退到普通DNS严格模式hostname必须使用指定加密DNS服务器否则无法联网在系统设置中这个功能藏在网络和互联网→私人DNS选项里。用户选择提供商主机名后可以输入像dns.google这样的DoTDNS over TLS服务商地址。有趣的是当你输入错误的主机名时Android会立即给出无法连接的提示这是因为系统在后台实时进行了预验证。2. 配置流程的代码实现解析2.1 设置界面的处理逻辑在Settings应用中私人DNS的配置界面由PrivateDnsModeDialogPreference这个自定义控件实现。当用户点击保存按钮时核心代码会执行以下操作Override public void onClick(DialogInterface dialog, int which) { if (which DialogInterface.BUTTON_POSITIVE) { final Context context getContext(); if (mMode.equals(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME)) { Settings.Global.putString(context.getContentResolver(), HOSTNAME_KEY, mEditText.getText().toString()); } Settings.Global.putString(context.getContentResolver(), MODE_KEY, mMode); } }这段代码将用户选择的值存入系统的SettingsProvider数据库具体存储在Settings.Global.PRIVATE_DNS_MODE保存模式选择off/opportunistic/hostnameSettings.Global.PRIVATE_DNS_SPECIFIER保存用户输入的DNS提供商主机名2.2 系统服务的响应机制当设置变化时系统核心服务会通过ContentObserver监听这些值的改变。ConnectivityService作为网络连接的中枢会通过DnsManager获取最新的配置public static PrivateDnsConfig getPrivateDnsConfig(ContentResolver cr) { final String mode getPrivateDnsMode(cr); final boolean useTls !TextUtils.isEmpty(mode) !PRIVATE_DNS_MODE_OFF.equals(mode); if (PRIVATE_DNS_MODE_PROVIDER_HOSTNAME.equals(mode)) { final String specifier getStringSetting(cr, PRIVATE_DNS_SPECIFIER); return new PrivateDnsConfig(specifier, null); } return new PrivateDnsConfig(useTls); }这个PrivateDnsConfig对象封装了所有必要的DNS配置信息包括是否使用TLS加密、主机名严格模式下以及解析出的IP地址。3. 网络监控与DNS处理的协作机制3.1 ConnectivityService的核心作用ConnectivityService是Android网络子系统的大脑。当网络状态变化时它的updateNetworkInfo方法会被触发private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) { if (!networkAgent.everConnected state NetworkInfo.State.CONNECTED) { networkAgent.everConnected true; handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig()); } }handlePerNetworkPrivateDnsConfig方法会做两件重要的事情通知NetworkMonitor进行DNS配置验证立即更新私有DNS设置即使严格模式下主机名尚未解析3.2 NetworkMonitor的验证流程NetworkMonitor是负责网络质量监测的服务组件。当收到私人DNS配置变更时public void notifyPrivateDnsSettingsChanged(PrivateDnsConfig newCfg) { removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED); sendMessage(CMD_PRIVATE_DNS_SETTINGS_CHANGED, newCfg); }在严格模式下它会启动一个复杂的验证过程解析用户输入的DNS主机名如果解析失败采用指数退避算法重试初始延迟约4.2秒解析成功后向这些IP地址发送特殊的DNS查询进行验证验证使用的是一组独特的探测域名格式为[随机UUID]-dnsotls-ds.metric.gstatic.com这样可以确保每次验证都是全新的请求避免缓存干扰。4. 底层实现与netd交互4.1 DNS配置的最终生效当NetworkMonitor完成验证后会通过回调通知ConnectivityServiceprivate void notifyPrivateDnsConfigResolved() { mCallback.notifyPrivateDnsConfigResolved(mPrivateDnsConfig.toParcel()); }ConnectivityService接着会调用DnsManager将配置推送到netd网络守护进程private void updatePrivateDns(NetworkAgentInfo nai, PrivateDnsConfig newCfg) { mDnsManager.updatePrivateDns(nai.network, newCfg); updateDnses(nai.linkProperties, null, nai.network.netId); }4.2 netd的实际处理在netd侧ResolverController负责应用这些配置int ResolverController::setResolverConfiguration( const ResolverParamsParcel resolverParams) { // 设置私有DNS服务器最多允许MAXNS个 std::vectorstd::string tlsServers resolverParams.tlsServers; if (tlsServers.size() MAXNS) { tlsServers.resize(MAXNS); } // 配置私有DNS const int err gPrivateDnsConfiguration.set(resolverParams.netId, fwmark.intValue, tlsServers, resolverParams.tlsName, tlsFingerprints); // 设置常规DNS服务器 return -resolv_set_nameservers_for_net(resolverParams.netId, server_ptrs.data(), server_ptrs.size(), domains_str.c_str(), res_params); }这个过程中有几个关键点严格模式下只使用用户指定的DNS服务器自动模式下会同时使用系统DNS和加密DNS每个网络netId都有独立的DNS配置5. 异常处理与恢复机制Android为私人DNS设计了完善的错误处理机制。当遇到网络问题时系统会触发多种恢复流程场景一DNS解析失败NetworkMonitor会记录失败次数并采用渐进式延迟重试1.5倍递增避免频繁请求造成电池消耗。场景二应用报告网络问题当应用调用ConnectivityManager#reportBadNetwork()时系统会暂时绕过私人DNS进行验证验证完成后如果是严格模式会重新评估私人DNS状态场景三设备策略限制企业设备可能通过DevicePolicyManager限制私人DNS设置if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_PRIVATE_DNS)) { Settings.Global.putString(mContext.getContentResolver(), Settings.Global.PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OPPORTUNISTIC); }这段代码展示了设备管理员如何强制恢复私人DNS设置为自动模式。6. 性能优化与隐私保护的平衡Android在实现私人DNS时做了很多优化考虑缓存策略DNS查询结果会被适当缓存但严格模式下的验证请求总是使用唯一的主机名确保每次都是新鲜查询。并行处理网络连接和DNS验证是并行进行的不会因为DNS查询阻塞网络可用性。回退机制自动模式下如果加密DNS不可用会无缝回退到普通DNS平衡了隐私和可用性。网络切换优化当用户在Wi-Fi和移动数据间切换时系统会智能地重用已验证的DNS配置减少重复验证的开销。在实际使用中开发者可以通过ConnectivityManager的API获取当前私人DNS状态但需要注意这些方法大多标记为hide意味着它们可能不适合普通应用使用。