平台
RK3588 + Android 12
问题描述
ConnectivityService是Android系统中负责处理网络连接的服务之一。它负责管理设备的网络连接状态,包括Wi-Fi、移动数据、蓝牙等。
在Android系统中,ConnectivityService提供了一些关键功能,包括但不限于:
- 网络状态监测: 它监测设备的网络状态,包括连接到的网络类型(如Wi-Fi、移动数据)、网络是否可用等。
- 网络类型切换: 当设备从一个网络切换到另一个网络时,ConnectivityService负责协调这个过程,以确保应用程序可以继续正常工作。
- 网络连接管理: 它允许应用程序查询当前网络连接的状态,并可以请求建立或中断网络连接。
- 网络通知: 它可以向应用程序发送广播通知,以通知它们有关网络状态的变化。
本文主要记录两点:
- Android 12 的ConnectivityService源码路径和机构的一些变化
- 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列表中连接状态的数据传递:
修改
- 修改认证地址
先看一段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>
- 自动确认保持连接
通知的发出:
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;
}
}
- 关闭网络测试
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)