Update multiple validation result to ConnectivityService

Once a network is determined to have partial connectivity, it
cannot go back to full connectivity without a disconnect. This
is because NetworkMonitor can only communicate either
PARTIAL_CONNECTIVITY or VALID, but not both. Thus, multiple
validation results allow ConnectivityService to know the real
network status.

Bug: 129662877
Bug: 130683832
Test: atest FrameworksNetTests
Test: atest NetworkStackTests
Test: atest --generate-new-metrics 50
NetworkStackTests:com.android.server.connectivity.NetworkMonitorTest
Test: Simulate partial connectvitiy
Change-Id: I406c9368617c03a2dd3ab15fb1f6dbf539d7c714
This commit is contained in:
Chiachang Wang
2019-05-23 16:29:30 +08:00
parent ac6f142334
commit eff1897661
2 changed files with 89 additions and 27 deletions

View File

@@ -25,8 +25,8 @@ import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.getNetworkTypeName; import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid; import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY; import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID; import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -2603,21 +2603,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2); final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
if (nai == null) break; if (nai == null) break;
final boolean partialConnectivity = final boolean wasPartial = nai.partialConnectivity;
(msg.arg1 == NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY) nai.partialConnectivity = ((msg.arg1 & NETWORK_VALIDATION_RESULT_PARTIAL) != 0);
|| (nai.networkMisc.acceptPartialConnectivity
&& nai.partialConnectivity);
// Once a network is determined to have partial connectivity, it cannot
// go back to full connectivity without a disconnect. This is because
// NetworkMonitor can only communicate either PARTIAL_CONNECTIVITY or VALID,
// but not both.
// TODO: Provide multi-testResult to improve the communication between
// ConnectivityService and NetworkMonitor, so that ConnectivityService could
// know the real status of network.
final boolean partialConnectivityChanged = final boolean partialConnectivityChanged =
(partialConnectivity && !nai.partialConnectivity); (wasPartial != nai.partialConnectivity);
final boolean valid = (msg.arg1 == NETWORK_TEST_RESULT_VALID); final boolean valid = ((msg.arg1 & NETWORK_VALIDATION_RESULT_VALID) != 0);
final boolean wasValidated = nai.lastValidated; final boolean wasValidated = nai.lastValidated;
final boolean wasDefault = isDefaultNetwork(nai); final boolean wasDefault = isDefaultNetwork(nai);
if (nai.everCaptivePortalDetected && !nai.captivePortalLoginNotified if (nai.everCaptivePortalDetected && !nai.captivePortalLoginNotified
@@ -2647,21 +2638,23 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai); if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai);
if (valid) { if (valid) {
handleFreshlyValidatedNetwork(nai); handleFreshlyValidatedNetwork(nai);
// Clear NO_INTERNET and LOST_INTERNET notifications if network becomes // Clear NO_INTERNET, PARTIAL_CONNECTIVITY and LOST_INTERNET
// valid. // notifications if network becomes valid.
mNotifier.clearNotification(nai.network.netId, mNotifier.clearNotification(nai.network.netId,
NotificationType.NO_INTERNET); NotificationType.NO_INTERNET);
mNotifier.clearNotification(nai.network.netId, mNotifier.clearNotification(nai.network.netId,
NotificationType.LOST_INTERNET); NotificationType.LOST_INTERNET);
mNotifier.clearNotification(nai.network.netId,
NotificationType.PARTIAL_CONNECTIVITY);
} }
} else if (partialConnectivityChanged) { } else if (partialConnectivityChanged) {
nai.partialConnectivity = partialConnectivity;
updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities); updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
} }
updateInetCondition(nai); updateInetCondition(nai);
// Let the NetworkAgent know the state of its network // Let the NetworkAgent know the state of its network
Bundle redirectUrlBundle = new Bundle(); Bundle redirectUrlBundle = new Bundle();
redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl); redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl);
// TODO: Evaluate to update partial connectivity to status to NetworkAgent.
nai.asyncChannel.sendMessage( nai.asyncChannel.sendMessage(
NetworkAgent.CMD_REPORT_NETWORK_STATUS, NetworkAgent.CMD_REPORT_NETWORK_STATUS,
(valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK), (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
@@ -3441,6 +3434,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Inform NetworkMonitor that partial connectivity is acceptable. This will likely // Inform NetworkMonitor that partial connectivity is acceptable. This will likely
// result in a partial connectivity result which will be processed by // result in a partial connectivity result which will be processed by
// maybeHandleNetworkMonitorMessage. // maybeHandleNetworkMonitorMessage.
//
// TODO: NetworkMonitor does not refer to the "never ask again" bit. The bit is stored
// per network. Therefore, NetworkMonitor may still do https probe.
try { try {
nai.networkMonitor().setAcceptPartialConnectivity(); nai.networkMonitor().setAcceptPartialConnectivity();
} catch (RemoteException e) { } catch (RemoteException e) {

View File

@@ -30,9 +30,12 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
import static android.net.ConnectivityManager.TYPE_NONE; import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
@@ -443,6 +446,16 @@ public class ConnectivityServiceTest {
} }
private class MockNetworkAgent { private class MockNetworkAgent {
private static final int VALIDATION_RESULT_BASE = NETWORK_VALIDATION_PROBE_DNS
| NETWORK_VALIDATION_PROBE_HTTP
| NETWORK_VALIDATION_PROBE_HTTPS;
private static final int VALIDATION_RESULT_VALID = VALIDATION_RESULT_BASE
| NETWORK_VALIDATION_RESULT_VALID;
private static final int VALIDATION_RESULT_PARTIAL = VALIDATION_RESULT_BASE
| NETWORK_VALIDATION_PROBE_FALLBACK
| NETWORK_VALIDATION_RESULT_PARTIAL;
private static final int VALIDATION_RESULT_INVALID = 0;
private final INetworkMonitor mNetworkMonitor; private final INetworkMonitor mNetworkMonitor;
private final NetworkInfo mNetworkInfo; private final NetworkInfo mNetworkInfo;
private final NetworkCapabilities mNetworkCapabilities; private final NetworkCapabilities mNetworkCapabilities;
@@ -460,17 +473,17 @@ public class ConnectivityServiceTest {
private String mRedirectUrl; private String mRedirectUrl;
private INetworkMonitorCallbacks mNmCallbacks; private INetworkMonitorCallbacks mNmCallbacks;
private int mNmValidationResult = NETWORK_TEST_RESULT_INVALID; private int mNmValidationResult = VALIDATION_RESULT_BASE;
private String mNmValidationRedirectUrl = null; private String mNmValidationRedirectUrl = null;
private boolean mNmProvNotificationRequested = false; private boolean mNmProvNotificationRequested = false;
void setNetworkValid() { void setNetworkValid() {
mNmValidationResult = NETWORK_TEST_RESULT_VALID; mNmValidationResult = VALIDATION_RESULT_VALID;
mNmValidationRedirectUrl = null; mNmValidationRedirectUrl = null;
} }
void setNetworkInvalid() { void setNetworkInvalid() {
mNmValidationResult = NETWORK_TEST_RESULT_INVALID; mNmValidationResult = VALIDATION_RESULT_INVALID;
mNmValidationRedirectUrl = null; mNmValidationRedirectUrl = null;
} }
@@ -480,7 +493,12 @@ public class ConnectivityServiceTest {
} }
void setNetworkPartial() { void setNetworkPartial() {
mNmValidationResult = NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY; mNmValidationResult = VALIDATION_RESULT_PARTIAL;
mNmValidationRedirectUrl = null;
}
void setNetworkPartialValid() {
mNmValidationResult = VALIDATION_RESULT_PARTIAL | VALIDATION_RESULT_VALID;
mNmValidationRedirectUrl = null; mNmValidationRedirectUrl = null;
} }
@@ -597,7 +615,7 @@ public class ConnectivityServiceTest {
private void onValidationRequested() { private void onValidationRequested() {
try { try {
if (mNmProvNotificationRequested if (mNmProvNotificationRequested
&& mNmValidationResult == NETWORK_TEST_RESULT_VALID) { && mNmValidationResult == VALIDATION_RESULT_VALID) {
mNmCallbacks.hideProvisioningNotification(); mNmCallbacks.hideProvisioningNotification();
mNmProvNotificationRequested = false; mNmProvNotificationRequested = false;
} }
@@ -2651,7 +2669,7 @@ public class ConnectivityServiceTest {
// With HTTPS probe disabled, NetworkMonitor should pass the network validation with http // With HTTPS probe disabled, NetworkMonitor should pass the network validation with http
// probe. // probe.
mWiFiNetworkAgent.setNetworkValid(); mWiFiNetworkAgent.setNetworkPartialValid();
// If the user chooses yes to use this partial connectivity wifi, switch the default // If the user chooses yes to use this partial connectivity wifi, switch the default
// network to wifi and check if wifi becomes valid or not. // network to wifi and check if wifi becomes valid or not.
mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */, mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */,
@@ -2748,6 +2766,54 @@ public class ConnectivityServiceTest {
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
} }
@Test
public void testCaptivePortalOnPartialConnectivity() throws RemoteException {
final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback);
final TestNetworkCallback validatedCallback = new TestNetworkCallback();
final NetworkRequest validatedRequest = new NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_VALIDATED).build();
mCm.registerNetworkCallback(validatedRequest, validatedCallback);
// Bring up a network with a captive portal.
// Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
String firstRedirectUrl = "http://example.com/firstPath";
mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl);
// Check that startCaptivePortalApp sends the expected command to NetworkMonitor.
mCm.startCaptivePortalApp(mWiFiNetworkAgent.getNetwork());
verify(mWiFiNetworkAgent.mNetworkMonitor, timeout(TIMEOUT_MS).times(1))
.launchCaptivePortalApp();
// Report that the captive portal is dismissed with partial connectivity, and check that
// callbacks are fired.
mWiFiNetworkAgent.setNetworkPartial();
mWiFiNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
waitForIdle();
captivePortalCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY,
mWiFiNetworkAgent);
// Report partial connectivity is accepted.
mWiFiNetworkAgent.setNetworkPartialValid();
mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */,
false /* always */);
waitForIdle();
mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
NetworkCapabilities nc =
validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY,
mWiFiNetworkAgent);
mCm.unregisterNetworkCallback(captivePortalCallback);
mCm.unregisterNetworkCallback(validatedCallback);
}
@Test @Test
public void testCaptivePortal() { public void testCaptivePortal() {
final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();