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

    <!-- 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

    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.

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

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

# 需注意,主板中有两个同名文件:
#	/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

    @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

<!-- 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

<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

<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
 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);
    }

点击通知后:

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

        <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

    @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

    @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);
        }

    }

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

    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;
		}
	}
  1. 关闭网络测试

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

//    public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode";
//    public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0;
//    public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1;

    private boolean getIsCaptivePortalCheckEnabled() {
        String symbol = CAPTIVE_PORTAL_MODE;
        int defaultValue = CAPTIVE_PORTAL_MODE_PROMPT;
        int mode = mDependencies.getSetting(mContext, symbol, defaultValue);
        return mode != CAPTIVE_PORTAL_MODE_IGNORE;
    }
    public static class Dependencies {
    	//....
        /**
         * Get the value of a global integer setting.
         * @param symbol Name of the setting
         * @param defaultValue Value to return if the setting is not defined.
         */
        public int getSetting(Context context, String symbol, int defaultValue) {
            return Settings.Global.getInt(context.getContentResolver(), symbol, defaultValue);
        }
adb shell settings put global captive_portal_mode 0

参考

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

### Android Framework WiFi Connected but No Internet Access Solution When an Android device connects to a WiFi network but cannot access the internet, several factors could contribute to this issue. Below is a detailed explanation of possible causes and solutions: #### 1. **Network Configuration Issues** The `ConnectivityManager` and `ConnectivityService` classes in Android are responsible for managing network connections[^1]. If the device connects to a WiFi network but lacks internet access, it may be due to incorrect network configuration. For example: - DHCP settings might not have been properly configured, preventing the device from obtaining an IP address. - DNS servers might not be reachable or incorrectly set. To resolve this: ```java ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo(); if (activeNetwork != null && !activeNetwork.isAvailable()) { // Network is connected but not available (no internet) } ``` This code snippet checks whether the active network is connected but unavailable for internet access[^1]. #### 2. **WiFi Captive Portal Detection** A captive portal is a web page that appears when connecting to a public WiFi network. If the device connects to such a network but does not authenticate through the portal, internet access will remain blocked. Android provides APIs like `CaptivePortalTracker` (deprecated) and `NetworkCapabilities` to detect and handle captive portals: ```java NetworkCapabilities networkCapabilities = connectivityManager.getNetworkCapabilities(connectivityManager.getActiveNetwork()); if (networkCapabilities != null && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { // Device has internet access } else if (networkCapabilities != null && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)) { // Device may require authentication via a captive portal } ``` This ensures the app can identify networks requiring additional authentication[^1]. #### 3. **DNS Resolution Problems** If DNS resolution fails, the device cannot resolve domain names into IP addresses, leading to no internet access. This issue can arise due to misconfigured DNS servers on the WiFi network. To fix DNS issues programmatically: ```java try { InetAddress inetAddress = InetAddress.getByName("www.google.com"); if (inetAddress != null) { // DNS resolution succeeded } } catch (UnknownHostException e) { // DNS resolution failed } ``` Additionally, you can configure custom DNS servers using the `NetworkFactory` and `Netd` APIs[^1]. #### 4. **Firewall or Router Restrictions** Sometimes, the WiFi router or firewall blocks internet access for specific devices. This could happen if the router's MAC filtering or IP restrictions are enabled. To troubleshoot: - Ensure the device's MAC address is allowed in the router's settings. - Check if the router enforces any port blocking rules that interfere with standard protocols (e.g., HTTP/HTTPS). #### 5. **Framework-Specific Issues** If the problem persists after addressing the above points, it might involve the specific framework being used. For instance: - If integrating third-party libraries (e.g., AWS SDK[^5] or Facebook SDK[^4]), ensure they do not override default network configurations. - Cross-platform frameworks like Xamarin Forms[^3] require careful handling of platform-specific APIs when accessing network resources. For example, in Xamarin.Android: ```csharp var connectivityManager = (ConnectivityManager)ApplicationContext.GetSystemService(Context.ConnectivityService); var activeNetwork = connectivityManager.ActiveNetworkInfo; if (activeNetwork != null && !activeNetwork.IsAvailable) { // Handle no internet scenario } ``` #### 6. **Debugging Tools** Use tools like `adb shell` and Linux Socket APIs[^1] to debug network issues: ```bash # Check current network state adb shell dumpsys netstats # Test DNS resolution adb shell ping www.google.com ``` These commands help diagnose connectivity problems at a lower level. ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值