如题,项目开发或者Google Nexus等亲儿子机器上,StatusBar经常会看到wifi和sim icon显示"x"号或者"!"号
首先给出结论,有以上的现象主要是因为默认访问Google服务器失败,Google源生逻辑则会在信号塔旁边显示"x"号或者"!"号提醒用户。
一般正常情况下,如果手机可以上网,就可以连接google服务器,但是由于国内对google做了限制,无法访问,才会出现这种情况。
下面则从代码逻辑中详细分析出现以上现象的原因
- 为了方便理解和说明,本文就以wifi icon为例子,sim icon流程和wifi icon完全是一致的
首先StatusBar中刷新wifi信号显示都是在WifiSignalController中完成的,下面我们来看下WifiSignalController是怎么处理wifi信号的
public WifiSignalController(Context context, boolean hasMobileData,
CallbackHandler callbackHandler, NetworkControllerImpl networkController) {
super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
callbackHandler, networkController);
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mWifiTracker = new WifiStatusTracker(mWifiManager);
mHasMobileData = hasMobileData;
Handler handler = new WifiHandler(Looper.getMainLooper());
mWifiChannel = new AsyncChannel();
Messenger wifiMessenger = mWifiManager.getWifiServiceMessenger();
if (wifiMessenger != null) {
mWifiChannel.connect(context, handler, wifiMessenger);
}
// WiFi only has one state.
mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup(
"Wi-Fi Icons",
WifiIcons.WIFI_SIGNAL_STRENGTH,
WifiIcons.QS_WIFI_SIGNAL_STRENGTH,
AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH,
WifiIcons.WIFI_NO_NETWORK,
WifiIcons.QS_WIFI_NO_NETWORK,
WifiIcons.WIFI_NO_NETWORK,
WifiIcons.QS_WIFI_NO_NETWORK,
AccessibilityContentDescriptions.WIFI_NO_CONNECTION
);
}
以上就是WifiSignalController的构造函数,也是最主要的初始化流程,在初始化的时候实例化了WifiManager和WifiStatusTracker等用于追踪wifi信息,最终通过WifiStatusTracker handleBroadcast处理并且刷新信号
public void handleBroadcast(Intent intent) {
String action = intent.getAction();
if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN);
enabled = state == WifiManager.WIFI_STATE_ENABLED;
} else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
final NetworkInfo networkInfo = (NetworkInfo)
intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
connecting = networkInfo != null && !networkInfo.isConnected()
&& networkInfo.isConnectedOrConnecting();
connected = networkInfo != null && networkInfo.isConnected();
// If Connected grab the signal strength and ssid.
if (connected) {
// try getting it out of the intent first
WifiInfo info = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO) != null
? (WifiInfo) intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO)
: mWifiManager.getConnectionInfo();
if (info != null) {
ssid = getSsid(info);
} else {
ssid = null;
}
} else if (!connected) {
ssid = null;
}
} else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
// Default to -200 as its below WifiManager.MIN_RSSI.
rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
level = WifiManager.calculateSignalLevel(rssi, 5);
}
}
本文我们重点关注的是构造函数中的mCurrentState.iconGroup,这个iconGroup就是wifi信号UI显示的所有要素
mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup(
"Wi-Fi Icons",
WifiIcons.WIFI_SIGNAL_STRENGTH,
WifiIcons.QS_WIFI_SIGNAL_STRENGTH,
AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH,
WifiIcons.WIFI_NO_NETWORK,
WifiIcons.QS_WIFI_NO_NETWORK,
WifiIcons.WIFI_NO_NETWORK,
WifiIcons.QS_WIFI_NO_NETWORK,
AccessibilityContentDescriptions.WIFI_NO_CONNECTION
);
其中最核心的就是WifiIcons.WIFI_SIGNAL_STRENGTH,我们来看下这个常量的定义,在WifiIcons中
public class WifiIcons {
static final int[][] WIFI_SIGNAL_STRENGTH = {
{ R.drawable.stat_sys_wifi_signal_0,
R.drawable.stat_sys_wifi_signal_1,
R.drawable.stat_sys_wifi_signal_2,
R.drawable.stat_sys_wifi_signal_3,
R.drawable.stat_sys_wifi_signal_4 },
{ R.drawable.stat_sys_wifi_signal_0_fully,
R.drawable.stat_sys_wifi_signal_1_fully,
R.drawable.stat_sys_wifi_signal_2_fully,
R.drawable.stat_sys_wifi_signal_3_fully,
R.drawable.stat_sys_wifi_signal_4_fully }
};
WIFI_SIGNAL_STRENGTH就是一个二维数组,这个数组里面就定义了不同状态栏下的wifi信号强度
这个数组就是本文的核心部分,第一个数组就是带有"x"号或者"!"号的wifi icon,第二个数组就是正常显示的wifi icon
下面我们就看下这个WIFI_SIGNAL_STRENGTH是在哪里被引用的
- 回到WifiSignalController中,看下wifi信号刷新的地方
@Override
public void notifyListeners(SignalCallback callback) {
// only show wifi in the cluster if connected or if wifi-only
boolean wifiVisible = mCurrentState.enabled
&& (mCurrentState.connected || !mHasMobileData);
String wifiDesc = wifiVisible ? mCurrentState.ssid : null;
boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
String contentDescription = getStringIfExists(getContentDescription());
if (mCurrentState.inetCondition == 0) {
contentDescription +=
("," + mContext.getString(R.string.accessibility_quick_settings_no_internet));
}
IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
IconState qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconId(),
contentDescription);
callback.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon,
ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut,
wifiDesc, mCurrentState.isTransient);
}
/**
* Extract wifi state directly from broadcasts about changes in wifi state.
*/
public void handleBroadcast(Intent intent) {
mWifiTracker.handleBroadcast(intent);
mCurrentState.enabled = mWifiTracker.enabled;
mCurrentState.connected = mWifiTracker.connected;
mCurrentState.ssid = mWifiTracker.ssid;
mCurrentState.rssi = mWifiTracker.rssi;
mCurrentState.level = mWifiTracker.level;
notifyListenersIfNecessary();
}
当handleBroadcast数据刷新执行完毕后,就会调用notifyListeners来刷新信号,其中
IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
就是wifi icon刷新的一个bean类
public static class IconState {
public final boolean visible;
public final int icon;
public final String contentDescription;
public IconState(boolean visible, int icon, String contentDescription) {
this.visible = visible;
this.icon = icon;
this.contentDescription = contentDescription;
}
public IconState(boolean visible, int icon, int contentDescription,
Context context) {
this(visible, icon, context.getString(contentDescription));
}
}
看到IconState的构造函数就相当清楚了,wifi icon其实就是这个icon,也就是上文的getCurrentIconId()
下面就来看下getCurrentIconId()实现的逻辑
- 在WifiSignalController的父类SignalController中可以看到
/**
* Gets the signal icon for SB based on current state of connected, enabled, and level.
*/
public int getCurrentIconId() {
if (mCurrentState.connected) {
return getIcons().mSbIcons[mCurrentState.inetCondition][mCurrentState.level];
} else if (mCurrentState.enabled) {
return getIcons().mSbDiscState;
} else {
return getIcons().mSbNullState;
}
}
public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0;
notifyListenersIfNecessary();
}
wifi信号数组主要是受这个mCurrentState.inetCondition变量控制,
当mCurrentState.inetCondition == 1是就显示正常的wifi icon,
而当mCurrentState.inetCondition == 0时,就显示本文中的带"x"号或者"!"的icon
接着往上找就会发现,在NetworkControllerImpl中pushConnectivityToSignals调用了
updateConnectivity
/**
* Pushes the current connectivity state to all SignalControllers.
*/
private void pushConnectivityToSignals() {
// We want to update all the icons, all at once, for any condition change
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
mobileSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
}
mWifiSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
mEthernetSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
}
下面就继续深究下mValidatedTransports变量的实现逻辑
- 在NetworkControllerImpl中,当接收到ConnectivityManager.CONNECTIVITY_ACTION或者ConnectivityManager.INET_CONDITION_ACTION广播的时候,触发了updateConnectivity的刷新
@Override
public void onReceive(Context context, Intent intent) {
if (CHATTY) {
Log.d(TAG, "onReceive: intent=" + intent);
}
final String action = intent.getAction();
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION) ||
action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
updateConnectivity();
}
/**
* Update the Inet conditions and what network we are connected to.
*/
private void updateConnectivity() {
mConnectedTransports.clear();
mValidatedTransports.clear();
for (NetworkCapabilities nc :
mConnectivityManager.getDefaultNetworkCapabilitiesForUser(mCurrentUserId)) {
for (int transportType : nc.getTransportTypes()) {
mConnectedTransports.set(transportType);
if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) {
mValidatedTransports.set(transportType);
}
}
}
if (CHATTY) {
Log.d(TAG, "updateConnectivity: mConnectedTransports=" + mConnectedTransports);
Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports);
}
mInetCondition = !mValidatedTransports.isEmpty();
pushConnectivityToSignals();
}
Bingo!!!到这里就找到Systemui中StatusBar最终显示带"x"号或者"!"的原因了,就是在
for (NetworkCapabilities nc :
mConnectivityManager.getDefaultNetworkCapabilitiesForUser(mCurrentUserId)) {
for (int transportType : nc.getTransportTypes()) {
mConnectedTransports.set(transportType);
if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) {
mValidatedTransports.set(transportType);
}
}
}
这个函数中,起决定性因素的nc.hasCapability(NET_CAPABILITY_VALIDATED)当这个函数返回false的时候就会导致在SignalController中mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0;
取到0,而当nc.hasCapability(NET_CAPABILITY_VALIDATED)== true,取到正确状态后,就取到1
有兴趣的朋友在这个函数里面加点log信息,逻辑就一目了然了
到这里Systemui分析就告一段落了,基本可以排除是Systemui逻辑问题,接下来就需要framework查收了
- 当然如果想进阶分析下framework为什么上报这种状态的话,可以跟着往下走
从上面的nc.hasCapability(NET_CAPABILITY_VALIDATED)开始
/**
* Tests for the presence of a capabilitity on this instance.
*
* @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be tested for.
* @return {@code true} if set on this instance.
*/
public boolean hasCapability(int capability) {
if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
return false;
}
return ((mNetworkCapabilities & (1 << capability)) != 0);
}
/**
* Adds the given capability to this {@code NetworkCapability} instance.
* Multiple capabilities may be applied sequentially. Note that when searching
* for a network to satisfy a request, all capabilities requested must be satisfied.
*
* @param capability the {@code NetworkCapabilities.NET_CAPABILITY_*} to be added.
* @return This NetworkCapabilities instance, to facilitate chaining.
* @hide
*/
public NetworkCapabilities addCapability(int capability) {
if (capability < MIN_NET_CAPABILITY || capability > MAX_NET_CAPABILITY) {
throw new IllegalArgumentException("NetworkCapability out of range");
}
mNetworkCapabilities |= 1 << capability;
return this;
}
在NetworkCapabilities中可以看到hasCapability主要是看mNetworkCapabilities的逻辑,理所当然的就需要看
addCapability函数的实现了,当务之急就是需要找到addCapability(NET_CAPABILITY_VALIDATED)调用的地方
继续查找源码,发现这个函数实现是在ConnectivityService中调用
/**
* Update the NetworkCapabilities for {@code networkAgent} to {@code networkCapabilities}
* augmented with any stateful capabilities implied from {@code networkAgent}
* (e.g., validated status and captive portal status).
*
* @param oldScore score of the network before any of the changes that prompted us
* to call this function.
* @param nai the network having its capabilities updated.
* @param networkCapabilities the new network capabilities.
*/
private void updateCapabilities(
int oldScore, NetworkAgentInfo nai, NetworkCapabilities networkCapabilities) {
// Once a NetworkAgent is connected, complain if some immutable capabilities are removed.
if (nai.everConnected && !nai.networkCapabilities.satisfiedByImmutableNetworkCapabilities(
networkCapabilities)) {
// TODO: consider not complaining when a network agent degrade its capabilities if this
// does not cause any request (that is not a listen) currently matching that agent to
// stop being matched by the updated agent.
String diff = nai.networkCapabilities.describeImmutableDifferences(networkCapabilities);
if (!TextUtils.isEmpty(diff)) {
Slog.wtf(TAG, "BUG: " + nai + " lost immutable capabilities:" + diff);
}
}
// Don't modify caller's NetworkCapabilities.
networkCapabilities = new NetworkCapabilities(networkCapabilities);
if (nai.lastValidated) {
networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
} else {
networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED);
}
private boolean maybeHandleNetworkMonitorMessage(Message msg) {
switch (msg.what) {
default:
return false;
case NetworkMonitor.EVENT_NETWORK_TESTED: {
final NetworkAgentInfo nai;
synchronized (mNetworkForNetId) {
nai = mNetworkForNetId.get(msg.arg2);
}
if (nai != null) {
final boolean valid =
(msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
final boolean wasValidated = nai.lastValidated;
if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") +
(msg.obj == null ? "" : " with redirect to " + (String)msg.obj));
if (valid != nai.lastValidated) {
final int oldScore = nai.getCurrentScore();
nai.lastValidated = valid;
nai.everValidated |= valid;
updateCapabilities(oldScore, nai, nai.networkCapabilities);
// If score has changed, rebroadcast to NetworkFactories. b/17726566
if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
}
.............
.............
.............
如上在NetworkMonitor.EVENT_NETWORK_TESTED message中
final boolean valid =
(msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
的值决定了nai.lastValidated的逻辑
这样的话逻辑就比较清晰了,接着继续查找NetworkMonitor
- 在NetworkMonitor中,sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
NETWORK_TEST_RESULT_VALID, mNetId, null))的地方如下
// Being in the ValidatedState State indicates a Network is:
// - Successfully validated, or
// - Wanted "as is" by the user, or
// - Does not satisfy the default NetworkRequest and so validation has been skipped.
private class ValidatedState extends State {
@Override
public void enter() {
maybeLogEvaluationResult(
networkEventType(validationStage(), EvaluationResult.VALIDATED));
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
NETWORK_TEST_RESULT_VALID, mNetId, null));
mValidations++;
}
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_NETWORK_CONNECTED:
transitionTo(mValidatedState);
return HANDLED;
default:
return NOT_HANDLED;
}
}
}
接着往下看,睁大眼睛看,精华的部分要出现了O(∩_∩)O哈哈~
// Note: This call to isCaptivePortal() could take up to a minute. Resolving the
// server's IP addresses could hit the DNS timeout, and attempting connections
// to each of the server's several IP addresses (currently one IPv4 and one
// IPv6) could each take SOCKET_TIMEOUT_MS. During this time this StateMachine
// will be unresponsive. isCaptivePortal() could be executed on another Thread
// if this is found to cause problems.
CaptivePortalProbeResult probeResult = isCaptivePortal();
if (probeResult.isSuccessful()) {
transitionTo(mValidatedState);
} else if (probeResult.isPortal()) {
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
NETWORK_TEST_RESULT_INVALID, mNetId, probeResult.redirectUrl));
mLastPortalProbeResult = probeResult;
transitionTo(mCaptivePortalState);
} else {
final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
sendMessageDelayed(msg, mReevaluateDelayMs);
logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
mConnectivityServiceHandler.sendMessage(obtainMessage(
EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, mNetId,
probeResult.redirectUrl));
if (mAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
// Don't continue to blame UID forever.
TrafficStats.clearThreadStatsUid();
}
mReevaluateDelayMs *= 2;
if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) {
mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS;
}
}
在这个函数中CaptivePortalProbeResult probeResult = isCaptivePortal();
这个关键的部分决定了当前的网络状态,然后根据状态isSuccessful isPortal send不同的message
继续isCaptivePortal()函数中的实现逻辑
@VisibleForTesting
protected CaptivePortalProbeResult isCaptivePortal() {
if (!mIsCaptivePortalCheckEnabled) {
validationLog("Validation disabled.");
return CaptivePortalProbeResult.SUCCESS;
}
URL pacUrl = null;
URL httpsUrl = mCaptivePortalHttpsUrl;
URL httpUrl = mCaptivePortalHttpUrl;
// On networks with a PAC instead of fetching a URL that should result in a 204
// response, we instead simply fetch the PAC script. This is done for a few reasons:
// 1. At present our PAC code does not yet handle multiple PACs on multiple networks
// until something like https://android-review.googlesource.com/#/c/115180/ lands.
// Network.openConnection() will ignore network-specific PACs and instead fetch
// using NO_PROXY. If a PAC is in place, the only fetch we know will succeed with
// NO_PROXY is the fetch of the PAC itself.
// 2. To proxy the generate_204 fetch through a PAC would require a number of things
// happen before the fetch can commence, namely:
// a) the PAC script be fetched
// b) a PAC script resolver service be fired up and resolve the captive portal
// server.
// Network validation could be delayed until these prerequisities are satisifed or
// could simply be left to race them. Neither is an optimal solution.
// 3. PAC scripts are sometimes used to block or restrict Internet access and may in
// fact block fetching of the generate_204 URL which would lead to false negative
// results for network validation.
final ProxyInfo proxyInfo = mNetworkAgentInfo.linkProperties.getHttpProxy();
if (proxyInfo != null && !Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) {
pacUrl = makeURL(proxyInfo.getPacFileUrl().toString());
if (pacUrl == null) {
return CaptivePortalProbeResult.FAILED;
}
}
if ((pacUrl == null) && (httpUrl == null || httpsUrl == null)) {
return CaptivePortalProbeResult.FAILED;
}
long startTime = SystemClock.elapsedRealtime();
final CaptivePortalProbeResult result;
if (pacUrl != null) {
result = sendDnsAndHttpProbes(null, pacUrl, ValidationProbeEvent.PROBE_PAC);
} else if (mUseHttps) {
result = sendParallelHttpProbes(proxyInfo, httpsUrl, httpUrl);
} else {
result = sendDnsAndHttpProbes(proxyInfo, httpUrl, ValidationProbeEvent.PROBE_HTTP);
}
long endTime = SystemClock.elapsedRealtime();
sendNetworkConditionsBroadcast(true /* response received */,
result.isPortal() /* isCaptivePortal */,
startTime, endTime);
return result;
}
在这个函数中,主要工作就是通过 send HTTP请求,最后通过返回值确认CaptivePortalProbeResult.SUCCESS还是CaptivePortalProbeResult.FAILED从而决定上面的sendMessage发送NETWORK_TEST_RESULT_INVALID还是
NETWORK_VALIDATION_FAILED
此函数中核心的两个URL httpsUrl = mCaptivePortalHttpsUrl和URL httpUrl = mCaptivePortalHttpUrl,他们的定义如下:
// Default configuration values for captive portal detection probes.
// TODO: append a random length parameter to the default HTTPS url.
// TODO: randomize browser version ids in the default User-Agent String.
private static final String DEFAULT_HTTPS_URL = "https://www.google.com/generate_204";
private static final String DEFAULT_HTTP_URL = "http://connectivitycheck.gstatic.com/generate_204";
private static String getCaptivePortalServerHttpsUrl(Context context) {
return getSetting(context, Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL);
}
public static String getCaptivePortalServerHttpUrl(Context context) {
return getSetting(context, Settings.Global.CAPTIVE_PORTAL_HTTP_URL, DEFAULT_HTTP_URL);
}
(首先给出结论,有以上的现象主要是因为默认访问Google服务器失败,Google源生逻辑则会在信号塔旁边显示"x"号或者"!"号提醒用户。)
到这里我们就找到了开头的结论的出处
写在最后
所以说,出现标题中的现象,其实按照源生逻辑是属于正常现象,因为你连不上谷爹的服务器了吗
当然如果项目开发中并不想看到此这个现象,想去除显示"x"号或者"!"号,那么我们再看了上面的原理之后,解决办法也就有了方案
- 头痛医头脚痛医脚方案: 在Systemui 中修改
static final int[][] WIFI_SIGNAL_STRENGTH = {
{ R.drawable.stat_sys_wifi_signal_0,
R.drawable.stat_sys_wifi_signal_1,
R.drawable.stat_sys_wifi_signal_2,
R.drawable.stat_sys_wifi_signal_3,
R.drawable.stat_sys_wifi_signal_4 },
{ R.drawable.stat_sys_wifi_signal_0_fully,
R.drawable.stat_sys_wifi_signal_1_fully,
R.drawable.stat_sys_wifi_signal_2_fully,
R.drawable.stat_sys_wifi_signal_3_fully,
R.drawable.stat_sys_wifi_signal_4_fully }
};
public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0;
notifyListenersIfNecessary();
}
可以把二维数组改成元素一致的,或者让mCurrentState.inetCondition == 1
当然此方案笔者是不大推荐的,只能当做是快速验证的临时方案
- 药到病除彻底根治方案:在framework中修改
@VisibleForTesting
protected CaptivePortalProbeResult isCaptivePortal() {
if (!mIsCaptivePortalCheckEnabled) {
validationLog("Validation disabled.");
return CaptivePortalProbeResult.SUCCESS;
}
...................
...................
...................
我们看到在NetworkMonitor中,其实framework给我们提供了一个mIsCaptivePortalCheckEnabled,让该函数直接返回SUCCESS.
mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.CAPTIVE_PORTAL_MODE, Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT)
!= Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE;
该变量就定义在NetworkMonitor构造函数中,看到这里就明了了,其实就是一个数据库值
/**
* Don't attempt to detect captive portals.
*
* @hide
*/
public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0;
/**
* When detecting a captive portal, display a notification that
* prompts the user to sign in.
*
* @hide
*/
public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1;
/**
* When detecting a captive portal, immediately disconnect from the
* network and do not reconnect to that network in the future.
*
* @hide
*/
public static final int CAPTIVE_PORTAL_MODE_AVOID = 2;
/**
* What to do when connecting a network that presents a captive portal.
* Must be one of the CAPTIVE_PORTAL_MODE_* constants above.
*
* The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT.
* @hide
*/
public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode";
/**
* Setting to turn off captive portal detection. Feature is enabled by
* default and the setting needs to be set to 0 to disable it.
*
* @deprecated use CAPTIVE_PORTAL_MODE_IGNORE to disable captive portal detection
* @hide
*/
@Deprecated
public static final String
CAPTIVE_PORTAL_DETECTION_ENABLED = "captive_portal_detection_enabled";
所以只需要在SettingsProvider的配置文件中,将该数据库配置下就可以了
<!-- Default for Settings.Global.CAPTIVE_PORTAL_MODE DISABLE -->
<setting type="global" name="captive_portal_mode" value="0"/>
<!-- Default for Settings.Global.CAPTIVE_PORTAL_MODE ENABLE -->
<setting type="global" name="captive_portal_mode" value="1"/>