Android12 WIFI 无法提供互联网连接

平台

RK3588 + Android 12

问题描述

ConnectivityService 是Android系统中负责处理网络连接的服务之一。它负责管理设备的网络连接状态,包括Wi-Fi、移动数据、蓝牙等。

在Android系统中,ConnectivityService提供了一些关键功能,包括但不限于:

  1. 网络状态监测: 它监测设备的网络状态,包括连接到的网络类型(如Wi-Fi、移动数据)、网络是否可用等。
  2. 网络类型切换: 当设备从一个网络切换到另一个网络时,ConnectivityService负责协调这个过程,以确保应用程序可以继续正常工作。
  3. 网络连接管理: 它允许应用程序查询当前网络连接的状态,并可以请求建立或中断网络连接。
  4. 网络通知: 它可以向应用程序发送广播通知,以通知它们有关网络状态的变化。

本文主要记录两点:

  1. Android 12 的ConnectivityService源码路径和机构的一些变化
  2. Wifi连接中的"已连接到设备,但无法提供互联网连接"问题.



    在连接到指定的WIFI热点后, 有时候会显示上面的提示信息, 大致的意思就是, 连上了但是上不了网

WIFI 设置

packages/apps/Settings/src/com/android/settings/network/NetworkProviderSettings.java

Can't provide internet 在哪里? 不在Settings 也不在 SettingsLib, 而是在WifiTrackerLib

frameworks/opt/net/wifi/libs/WifiTrackerLib/res/values/strings.xml

xml 复制代码
    <!-- Summary for connected wifi network without internet [CHAR LIMIT=NONE] -->
    <string name="wifitrackerlib_wifi_connected_cannot_provide_internet">
        Connected to device. Can\'t provide internet.</string>

frameworks/opt/net/wifi/libs/WifiTrackerLib/src/com/android/wifitrackerlib/Utils.java
wifitrackerlib_wifi_connected_cannot_provide_internet

java 复制代码
    static String getCurrentNetworkCapabilitiesInformation(Context context,
            NetworkCapabilities networkCapabilities) {
        if (context == null || networkCapabilities == null) {
            return "";
        }

        if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL)) {
            return context.getString(context.getResources()
                    .getIdentifier("network_available_sign_in", "string", "android"));
        }

        if (networkCapabilities.hasCapability(
                NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY)) {
            return context.getString(R.string.wifitrackerlib_wifi_limited_connection);
        }

        if (!networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
            if (networkCapabilities.isPrivateDnsBroken()) {
                return context.getString(R.string.wifitrackerlib_private_dns_broken);
            }
            return context.getString(
                R.string.wifitrackerlib_wifi_connected_cannot_provide_internet);
        }
        return "";
    }

ConnectivityManager.java 有几个, 需要注意不要改错

frameworks/layoutlib/bridge/src/android/net/ConnectivityManager.java

packages/modules/Connectivity/core/java/android/net/ConnectivityManager.java

packages/modules/Connectivity/framework/src/android/net/ConnectivityManager.java

ConnectivityService.java 同样有多个

packages/modules/Connectivity/service /src/com/android/server/ConnectivityService.java

packages/modules/Connectivity/services/core/java/com/android/server/ConnectivityService.java

如何单编译ConnectivityService 注意, 使用的是service.

bash 复制代码
mmm packages/modules/Connectivity/service/:service-connectivity
# 将会生成文件:
out/target/product/rk3588_s/system/apex/com.android.tethering/javalib/service-connectivity.jar

替换主板中的文件并重启即可

bash 复制代码
# 需注意,主板中有两个同名文件:
#	/apex/com.android.tethering/javalib/service-connectivity.jar
#	/system/apex/com.android.tethering.inprocess/javalib/service-connectivity.jar
# 不要搞错路径
## root 和 remount 之后
adb push service-connectivity.jar /system/apex/com.android.tethering.inprocess/javalib/

在 Android 源代码中,com.android.tethering 通常用于处理网络共享(Tethering)的功能。网络共享允许设备通过不同的网络接口(如移动数据、Wi-Fi或蓝牙)与其他设备共享其网络连接。

具体来说,com.android.tethering 是 Android 框架的一部分,负责实现和管理网络共享的相关功能。这包括创建和管理 Wi-Fi 热点、USB 网络共享以及蓝牙网络共享等。

简单整理下Wifi列表中连接状态的数据传递:
NetworkProviderSettings WifiPickerTracker BaseWifiTracker ConnectivityManager CallbackHandler ConnectivityService NetworkStateTrackerHandler packages/apps/Settings getWifiEntries frameworks/opt/net/wifi/libs/WifiTrackerLib onStart packages/modules/Connectivity/service registerNetworkCallback sendRequestForNetwork trigger maybeHandleNetworkMonitorMessage handleNetworkTested updateCapabilities notifyNetworkCallbacks callCallbackForRequest handleMessage(CALLBACK_CAP_CHANGED) onCapabilitiesChanged handleNetworkCapabilitiesChanged NetworkProviderSettings WifiPickerTracker BaseWifiTracker ConnectivityManager CallbackHandler ConnectivityService NetworkStateTrackerHandler

修改

  1. 修改认证地址
    先看一段LOG:

    PROBE_DNS www.google.cn 5055ms OK 220.181.174.226
    PROBE_DNS www.google.cn 5059ms OK 220.181.174.226
    PROBE_HTTP http://www.google.cn/generate_204 time=126ms ret=204 request={Connection=[close], User-Agent=[Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.32 Safari/537.36]} headers={null=[HTTP/1.1 204 No Content], Connection=[close], Content-Length=[0], Cross-Origin-Resource-Policy=[cross-origin], Date=[Tue, 05 Dec 2023 01:21:40 GMT], X-Android-Received-Millis=[1701739300483], X-Android-Response-Source=[NETWORK 204], X-Android-Selected-Protocol=[http/1.1], X-Android-Sent-Millis=[1701739300402]}
    PROBE_HTTPS https://www.google.cn/generate_204 time=284ms ret=204 request={Connection=[close], User-Agent=[Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.32 Safari/537.36]} headers={null=[HTTP/1.1 204 No Content], Alt-Svc=[h3=":443"; ma=2592000,h3-29=":443"; ma=2592000], Connection=[close], Content-Length=[0], Cross-Origin-Resource-Policy=[cross-origin], Date=[Tue, 05 Dec 2023 01:21:40 GMT], X-Android-Received-Millis=[1701739300641], X-Android-Response-Source=[NETWORK 204], X-Android-Selected-Protocol=[http/1.1], X-Android-Sent-Millis=[1701739300521]}
    PROBE_FALLBACK http://www.google.com/gen_204 Probe failed with exception java.net.ConnectException: Failed to connect to www.google.com/4.78.139.54:80
    PROBE_FALLBACK http://www.google.com/gen_204 Probe failed with exception java.net.SocketTimeoutException: failed to connect to www.google.com/31.13.94.37 (port 80) from /192.168.1.86 (port 51118) after 10000ms

packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java

java 复制代码
    @VisibleForTesting
    public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network,
            IpConnectivityLog logger, SharedLog validationLogs,
            @NonNull NetworkStackServiceManager serviceManager, Dependencies deps,
            @Nullable TcpSocketTracker tst) {
        // Add suffix indicating which NetworkMonitor we're talking about.
        super(TAG + "/" + network.toString());

        // ...
        mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled();
        mPrivateIpNoInternetEnabled = getIsPrivateIpNoInternetEnabled();
        mMetricsEnabled = deps.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY,
                NetworkStackUtils.VALIDATION_METRICS_VERSION, true /* defaultEnabled */);
        mUseHttps = getUseHttpsValidation();
        mCaptivePortalUserAgent = getCaptivePortalUserAgent();
        mCaptivePortalHttpsUrls = makeCaptivePortalHttpsUrls();
		android.util.Log.d(TAG, "mCaptivePortalHttpsUrls[0]=" + mCaptivePortalHttpsUrls[0].toString());
        mCaptivePortalHttpUrls = makeCaptivePortalHttpUrls();
        mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls();
        mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs();
        //....
	}
    private URL[] makeCaptivePortalHttpsUrls() {
        final URL testUrl = getTestUrl(TEST_CAPTIVE_PORTAL_HTTPS_URL);
        if (testUrl != null) return new URL[] { testUrl };

        final String firstUrl = getCaptivePortalServerHttpsUrl();
        try {
            final URL[] settingProviderUrls =
                combineCaptivePortalUrls(firstUrl, CAPTIVE_PORTAL_OTHER_HTTPS_URLS);
            // firstUrl will at least be default configuration, so default value in
            // getProbeUrlArrayConfig is actually never used.
            return getProbeUrlArrayConfig(settingProviderUrls,
                    R.array.config_captive_portal_https_urls,
                    DEFAULT_CAPTIVE_PORTAL_HTTPS_URLS, this::makeURL);
        } catch (Exception e) {
            // Don't let a misconfiguration bootloop the system.
            Log.e(TAG, "Error parsing configured https URLs", e);
            // Ensure URL aligned with legacy configuration.
            return new URL[]{makeURL(firstUrl)};
        }
    }
    private String getCaptivePortalServerHttpsUrl() {
        return getSettingFromResource(mCustomizedContext,
                R.string.config_captive_portal_https_url, CAPTIVE_PORTAL_HTTPS_URL,
                mCustomizedContext.getResources().getString(
                R.string.default_captive_portal_https_url));
    }

修改配置文件即可:

packages/modules/NetworkStack/res/values/config.xml

xml 复制代码
<!-- HTTP URL for network validation, to use for detecting captive portals. -->
    <!-- default_captive_portal_http_url is not configured as overlayable so
         OEMs that wish to change captive_portal_http_url configuration must
         do so via configuring runtime resource overlay to
         config_captive_portal_http_url and *NOT* by changing or overlaying
         this resource. It will break if the enforcement of overlayable starts.
         -->
    <string name="default_captive_portal_http_url" translatable="false">http://connectivitycheck.gstatic.com/generate_204</string>
    <!-- HTTPS URL for network validation, to use for confirming internet connectivity. -->
    <!-- default_captive_portal_https_url is not configured as overlayable so
         OEMs that wish to change captive_portal_https_url configuration must
         do so via configuring runtime resource overlay to
         config_captive_portal_https_url and *NOT* by changing or overlaying
         this resource. It will break if the enforcement of overlayable starts.
         -->
    <string name="default_captive_portal_https_url" translatable="false">https://www.google.com/generate_204</string>

    <!-- List of fallback URLs to use for detecting captive portals. -->
    <!-- default_captive_portal_fallback_urls is not configured as overlayable
         so OEMs that wish to change captive_portal_fallback_urls configuration
         must do so via configuring runtime resource overlay to
         config_captive_portal_fallback_urls and *NOT* by changing or overlaying
         this resource. It will break if the enforcement of overlayable starts.
         -->
    <string-array name="default_captive_portal_fallback_urls" translatable="false">
        <item>http://www.google.com/gen_204</item>
        <item>http://play.googleapis.com/generate_204</item>
    </string-array>
    <!-- Configuration hooks for the above settings.
         Empty by default but may be overridden by RROs. -->
    <integer name="config_captive_portal_dns_probe_timeout"></integer>
    <!--suppress CheckTagEmptyBody: overlayable resource to use as configuration hook -->
    <string name="config_captive_portal_http_url" translatable="false"></string>
    <!--suppress CheckTagEmptyBody: overlayable resource to use as configuration hook -->
    <string name="config_captive_portal_https_url" translatable="false"></string>

前面改了不生效, 注意:OVERLAY

device/rockchip/common/overlay/packages/modules/NetworkStack/res/values/config.xml

xml 复制代码
<string name="config_captive_portal_http_url" translatable="false">http://www.google.cn/generate_204</string>

编译 :mmm packages/modules/NetworkStack/:InProcessNetworkStack

还可以尝试使用RRO的方式

vendor/rockchip/common/gms/RockchipNetworkStackConfigOverlay/res/values/config.xml

xml 复制代码
<string name="config_captive_portal_https_url" translatable="false">https://www.google.cn/generate_204</string>
  1. 自动确认保持连接
    通知的发出:

    showNotification tag=ConnectivityNotification:100 event=NO_INTERNET transport=WLAN name=XXXX highPriority=true

java 复制代码
 private void showNetworkNotification(NetworkAgentInfo nai, NotificationType type) {
        final String action;
        final boolean highPriority;
        switch (type) {
            case NO_INTERNET:
                action = ConnectivityManager.ACTION_PROMPT_UNVALIDATED;
                // High priority because it is only displayed for explicitly selected networks.
                highPriority = true;
                break;
            case PRIVATE_DNS_BROKEN:
                action = Settings.ACTION_WIRELESS_SETTINGS;
                // High priority because we should let user know why there is no internet.
                highPriority = true;
                break;
            case LOST_INTERNET:
                action = ConnectivityManager.ACTION_PROMPT_LOST_VALIDATION;
                // High priority because it could help the user avoid unexpected data usage.
                highPriority = true;
                break;
            case PARTIAL_CONNECTIVITY:
                action = ConnectivityManager.ACTION_PROMPT_PARTIAL_CONNECTIVITY;
                // Don't bother the user with a high-priority notification if the network was not
                // explicitly selected by the user.
                highPriority = nai.networkAgentConfig.explicitlySelected;
                break;
            default:
                Log.wtf(TAG, "Unknown notification type " + type);
                return;
        }

        Intent intent = new Intent(action);
        if (type != NotificationType.PRIVATE_DNS_BROKEN) {
            intent.putExtra(ConnectivityManager.EXTRA_NETWORK, nai.network);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            // Some OEMs have their own Settings package. Thus, need to get the current using
            // Settings package name instead of just use default name "com.android.settings".
            final String settingsPkgName = getSettingsPackageName(mContext.getPackageManager());
            intent.setClassName(settingsPkgName,
                    settingsPkgName + ".wifi.WifiNoInternetDialog");
        }

        PendingIntent pendingIntent = PendingIntent.getActivity(
                mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
                0 /* requestCode */,
                intent,
                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);

        mNotifier.showNotification(
                nai.network.getNetId(), type, nai, null, pendingIntent, highPriority);
    }

点击通知后:

log 复制代码
START u0 {act=android.net.action.PROMPT_UNVALIDATED flg=0x10000000 cmp=com.android.settings/.wifi.WifiNoInternetDialog (has extras)} from uid 1000

看Settings的清单:

packages/apps/Settings/AndroidManifest.xml

xml 复制代码
        <activity android:name=".wifi.WifiNoInternetDialog"
                  android:clearTaskOnLaunch="true"
                  android:excludeFromRecents="true"
                  android:exported="true"
                  android:permission="android.permission.NETWORK_STACK"
                  android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight">
            <!-- TODO: Consider removing below two intent filters.
                 It seems like below two intent filters can be removed because when the notification
                 is clicked, this activity will be launched anyway. -->
            <intent-filter>
                <action android:name="android.net.action.PROMPT_UNVALIDATED" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.net.action.PROMPT_LOST_VALIDATION" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
                android:value="true" />
        </activity>

packages/apps/Settings/src/com/android/settings/wifi/WifiNoInternetDialog.java

java 复制代码
    @Override
    public void onClick(DialogInterface dialog, int which) {
        if (which != BUTTON_NEGATIVE && which != BUTTON_POSITIVE) return;
        final boolean always = mAlwaysAllow.isChecked();
        final String what, action;

        mButtonClicked = true;

        if (ACTION_PROMPT_UNVALIDATED.equals(mAction)) {
            what = "NO_INTERNET";
            final boolean accept = (which == BUTTON_POSITIVE);
            action = (accept ? "Connect" : "Ignore");
            mCM.setAcceptUnvalidated(mNetwork, accept, always);
        } else if (ACTION_PROMPT_PARTIAL_CONNECTIVITY.equals(mAction)) {
            what = "PARTIAL_CONNECTIVITY";
            final boolean accept = (which == BUTTON_POSITIVE);
            action = (accept ? "Connect" : "Ignore");
            mCM.setAcceptPartialConnectivity(mNetwork, accept, always);
        } else {
            what = "LOST_INTERNET";
            final boolean avoid = (which == BUTTON_POSITIVE);
            action = (avoid ? "Switch away" : "Get stuck");
            if (always) {
                Settings.Global.putString(mAlertParams.mContext.getContentResolver(),
                        Settings.Global.NETWORK_AVOID_BAD_WIFI, avoid ? "1" : "0");
            } else if (avoid) {
                mCM.setAvoidUnvalidated(mNetwork);
            }
        }
        Log.d(TAG, what + ": " + action +  " network=" + mNetwork +
                (always ? " and remember" : ""));
    }

packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java

java 复制代码
    @Override
    public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
        enforceNetworkStackSettingsOrSetup();
        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_ACCEPT_UNVALIDATED,
                encodeBool(accept), encodeBool(always), network));
    }
    
    private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) {
        if (DBG) log("handleSetAcceptUnvalidated network=" + network +
                " accept=" + accept + " always=" + always);

        NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
        if (nai == null) {
            // Nothing to do.
            return;
        }

        if (nai.everValidated) {
            // The network validated while the dialog box was up. Take no action.
            return;
        }

        if (!nai.networkAgentConfig.explicitlySelected) {
            Log.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network");
        }

        if (accept != nai.networkAgentConfig.acceptUnvalidated) {
            nai.networkAgentConfig.acceptUnvalidated = accept;
            // If network becomes partial connectivity and user already accepted to use this
            // network, we should respect the user's option and don't need to popup the
            // PARTIAL_CONNECTIVITY notification to user again.
            nai.networkAgentConfig.acceptPartialConnectivity = accept;
            nai.updateScoreForNetworkAgentUpdate();
            rematchAllNetworksAndRequests();
        }

        if (always) {
            nai.onSaveAcceptUnvalidated(accept);
        }

        if (!accept) {
            // Tell the NetworkAgent to not automatically reconnect to the network.
            nai.onPreventAutomaticReconnect();
            // Teardown the network.
            teardownUnneededNetwork(nai);
        }

    }

增加属性控制, 不发送通知, 并执行保持连接:

java 复制代码
    private void handlePromptUnvalidated(Network network) {
		//Force keep-connect for network
		if("1".equals(android.os.SystemProperties.get("persist.sys.keepNetworkConnect"))){
			handleSetAcceptUnvalidated(network, true, true);
			return;
		}
	}

参考

ConnectivityService处理wifi连接
android 网络连接受限解决
android 网络重新连接时BaseActivity处理 android网络连接受限
android wif 去掉 双引号 原生安卓去掉wifi叉号
AndroidQ RRO(Runtime Resource Overlay)机制(1)

相关推荐
姜行运40 分钟前
数据结构【二叉搜索树(BST)】
android·数据结构·c++·c#
JhonKI8 小时前
【MySQL】存储引擎 - CSV详解
android·数据库·mysql
开开心心_Every9 小时前
手机隐私数据彻底删除工具:回收或弃用手机前防数据恢复
android·windows·python·搜索引擎·智能手机·pdf·音视频
大G哥9 小时前
Kotlin Lambda语法错误修复
android·java·开发语言·kotlin
鸿蒙布道师13 小时前
鸿蒙NEXT开发动画案例2
android·ios·华为·harmonyos·鸿蒙系统·arkui·huawei
androidwork13 小时前
Kotlin Android工程Mock数据方法总结
android·开发语言·kotlin
xiangxiongfly91515 小时前
Android setContentView()源码分析
android·setcontentview
人间有清欢16 小时前
Android开发补充内容
android·okhttp·rxjava·retrofit·hilt·jetpack compose
人间有清欢17 小时前
Android开发报错解决
android
每次的天空18 小时前
Android学习总结之kotlin协程面试篇
android·学习·kotlin