am f50ac2e7
: am cfedbc43
: am 7f9e3274
: am bbce221e
: Merge "Fallback to Cellular if WiFi fails to validate" into mnc-dev
* commit 'f50ac2e7bb0842bbb30f69c898a95f894b5d506c': Fallback to Cellular if WiFi fails to validate
This commit is contained in:
@ -2064,12 +2064,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
// if it's awaiting captive portal login, or if validation failed), this
|
||||
// may trigger a re-evaluation of the network.
|
||||
private void unlinger(NetworkAgentInfo nai) {
|
||||
nai.networkLingered.clear();
|
||||
if (!nai.lingering) return;
|
||||
nai.lingering = false;
|
||||
if (VDBG) log("Canceling linger of " + nai.name());
|
||||
// If network has never been validated, it cannot have been lingered, so don't bother
|
||||
// needlessly triggering a re-evaluation.
|
||||
if (!nai.everValidated) return;
|
||||
nai.networkLingered.clear();
|
||||
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
|
||||
}
|
||||
|
||||
@ -2224,43 +2222,28 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
|
||||
// Is nai unneeded by all NetworkRequests (and should be disconnected)?
|
||||
// For validated Networks this is simply whether it is satsifying any NetworkRequests.
|
||||
// For unvalidated Networks this is whether it is satsifying any NetworkRequests or
|
||||
// were it to become validated, would it have a chance of satisfying any NetworkRequests.
|
||||
// This is whether it is satisfying any NetworkRequests or were it to become validated,
|
||||
// would it have a chance of satisfying any NetworkRequests.
|
||||
private boolean unneeded(NetworkAgentInfo nai) {
|
||||
if (!nai.created || nai.isVPN() || nai.lingering) return false;
|
||||
boolean unneeded = true;
|
||||
if (nai.everValidated) {
|
||||
for (int i = 0; i < nai.networkRequests.size() && unneeded; i++) {
|
||||
final NetworkRequest nr = nai.networkRequests.valueAt(i);
|
||||
try {
|
||||
if (isRequest(nr)) unneeded = false;
|
||||
} catch (Exception e) {
|
||||
loge("Request " + nr + " not found in mNetworkRequests.");
|
||||
loge(" it came from request list of " + nai.name());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
|
||||
// If this Network is already the highest scoring Network for a request, or if
|
||||
// there is hope for it to become one if it validated, then it is needed.
|
||||
if (nri.isRequest && nai.satisfies(nri.request) &&
|
||||
(nai.networkRequests.get(nri.request.requestId) != null ||
|
||||
// Note that this catches two important cases:
|
||||
// 1. Unvalidated cellular will not be reaped when unvalidated WiFi
|
||||
// is currently satisfying the request. This is desirable when
|
||||
// cellular ends up validating but WiFi does not.
|
||||
// 2. Unvalidated WiFi will not be reaped when validated cellular
|
||||
// is currently satsifying the request. This is desirable when
|
||||
// WiFi ends up validating and out scoring cellular.
|
||||
mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() <
|
||||
nai.getCurrentScoreAsValidated())) {
|
||||
unneeded = false;
|
||||
break;
|
||||
}
|
||||
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
|
||||
// If this Network is already the highest scoring Network for a request, or if
|
||||
// there is hope for it to become one if it validated, then it is needed.
|
||||
if (nri.isRequest && nai.satisfies(nri.request) &&
|
||||
(nai.networkRequests.get(nri.request.requestId) != null ||
|
||||
// Note that this catches two important cases:
|
||||
// 1. Unvalidated cellular will not be reaped when unvalidated WiFi
|
||||
// is currently satisfying the request. This is desirable when
|
||||
// cellular ends up validating but WiFi does not.
|
||||
// 2. Unvalidated WiFi will not be reaped when validated cellular
|
||||
// is currently satsifying the request. This is desirable when
|
||||
// WiFi ends up validating and out scoring cellular.
|
||||
mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() <
|
||||
nai.getCurrentScoreAsValidated())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return unneeded;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) {
|
||||
@ -4004,29 +3987,29 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
* augmented with any stateful capabilities implied from {@code networkAgent}
|
||||
* (e.g., validated status and captive portal status).
|
||||
*
|
||||
* @param networkAgent the network having its capabilities updated.
|
||||
* @param nai the network having its capabilities updated.
|
||||
* @param networkCapabilities the new network capabilities.
|
||||
*/
|
||||
private void updateCapabilities(NetworkAgentInfo networkAgent,
|
||||
NetworkCapabilities networkCapabilities) {
|
||||
private void updateCapabilities(NetworkAgentInfo nai, NetworkCapabilities networkCapabilities) {
|
||||
// Don't modify caller's NetworkCapabilities.
|
||||
networkCapabilities = new NetworkCapabilities(networkCapabilities);
|
||||
if (networkAgent.lastValidated) {
|
||||
if (nai.lastValidated) {
|
||||
networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
|
||||
} else {
|
||||
networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED);
|
||||
}
|
||||
if (networkAgent.lastCaptivePortalDetected) {
|
||||
if (nai.lastCaptivePortalDetected) {
|
||||
networkCapabilities.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
|
||||
} else {
|
||||
networkCapabilities.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
|
||||
}
|
||||
if (!Objects.equals(networkAgent.networkCapabilities, networkCapabilities)) {
|
||||
synchronized (networkAgent) {
|
||||
networkAgent.networkCapabilities = networkCapabilities;
|
||||
if (!Objects.equals(nai.networkCapabilities, networkCapabilities)) {
|
||||
final int oldScore = nai.getCurrentScore();
|
||||
synchronized (nai) {
|
||||
nai.networkCapabilities = networkCapabilities;
|
||||
}
|
||||
rematchAllNetworksAndRequests(networkAgent, networkAgent.getCurrentScore());
|
||||
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_CAP_CHANGED);
|
||||
rematchAllNetworksAndRequests(nai, oldScore);
|
||||
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4256,9 +4239,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
// Linger any networks that are no longer needed.
|
||||
for (NetworkAgentInfo nai : affectedNetworks) {
|
||||
if (nai.everValidated && unneeded(nai)) {
|
||||
if (nai.lingering) {
|
||||
// Already lingered. Nothing to do. This can only happen if "nai" is in
|
||||
// "affectedNetworks" twice. The reasoning being that to get added to
|
||||
// "affectedNetworks", "nai" must have been satisfying a NetworkRequest
|
||||
// (i.e. not lingered) so it could have only been lingered by this loop.
|
||||
// unneeded(nai) will be false and we'll call unlinger() below which would
|
||||
// be bad, so handle it here.
|
||||
} else if (unneeded(nai)) {
|
||||
linger(nai);
|
||||
} else {
|
||||
// Clear nai.networkLingered we might have added above.
|
||||
unlinger(nai);
|
||||
}
|
||||
}
|
||||
@ -4292,7 +4283,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(),
|
||||
oldDefaultNetwork, true);
|
||||
}
|
||||
mDefaultInetConditionPublished = newNetwork.everValidated ? 100 : 0;
|
||||
mDefaultInetConditionPublished = newNetwork.lastValidated ? 100 : 0;
|
||||
mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork);
|
||||
notifyLockdownVpn(newNetwork);
|
||||
}
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.server.connectivity;
|
||||
|
||||
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
@ -39,6 +41,62 @@ import java.util.Comparator;
|
||||
* AsyncChannel/messenger for reaching that NetworkAgent and lists of NetworkRequests
|
||||
* interested in using it. Default sort order is descending by score.
|
||||
*/
|
||||
// States of a network:
|
||||
// --------------------
|
||||
// 1. registered, uncreated, disconnected, unvalidated
|
||||
// This state is entered when a NetworkFactory registers a NetworkAgent in any state except
|
||||
// the CONNECTED state.
|
||||
// 2. registered, uncreated, connected, unvalidated
|
||||
// This state is entered when a registered NetworkAgent transitions to the CONNECTED state
|
||||
// ConnectivityService will tell netd to create the network and immediately transition to
|
||||
// state #3.
|
||||
// 3. registered, created, connected, unvalidated
|
||||
// If this network can satsify the default NetworkRequest, then NetworkMonitor will
|
||||
// probe for Internet connectivity.
|
||||
// If this network cannot satisfy the default NetworkRequest, it will immediately be
|
||||
// transitioned to state #4.
|
||||
// A network may remain in this state if NetworkMonitor fails to find Internet connectivity,
|
||||
// for example:
|
||||
// a. a captive portal is present, or
|
||||
// b. a WiFi router whose Internet backhaul is down, or
|
||||
// c. a wireless connection stops transfering packets temporarily (e.g. device is in elevator
|
||||
// or tunnel) but does not disconnect from the AP/cell tower, or
|
||||
// d. a stand-alone device offering a WiFi AP without an uplink for configuration purposes.
|
||||
// 4. registered, created, connected, validated
|
||||
//
|
||||
// The device's default network connection:
|
||||
// ----------------------------------------
|
||||
// Networks in states #3 and #4 may be used as a device's default network connection if they
|
||||
// satisfy the default NetworkRequest.
|
||||
// A network, that satisfies the default NetworkRequest, in state #4 should always be chosen
|
||||
// in favor of a network, that satisfies the default NetworkRequest, in state #3.
|
||||
// When deciding between two networks, that both satisfy the default NetworkRequest, to select
|
||||
// for the default network connection, the one with the higher score should be chosen.
|
||||
//
|
||||
// When a network disconnects:
|
||||
// ---------------------------
|
||||
// If a network's transport disappears, for example:
|
||||
// a. WiFi turned off, or
|
||||
// b. cellular data turned off, or
|
||||
// c. airplane mode is turned on, or
|
||||
// d. a wireless connection disconnects from AP/cell tower entirely (e.g. device is out of range
|
||||
// of AP for an extended period of time, or switches to another AP without roaming)
|
||||
// then that network can transition from any state (#1-#4) to unregistered. This happens by
|
||||
// the transport disconnecting their NetworkAgent's AsyncChannel with ConnectivityManager.
|
||||
// ConnectivityService also tells netd to destroy the network.
|
||||
//
|
||||
// When ConnectivityService disconnects a network:
|
||||
// -----------------------------------------------
|
||||
// If a network has no chance of satisfying any requests (even if it were to become validated
|
||||
// and enter state #4), ConnectivityService will disconnect the NetworkAgent's AsyncChannel.
|
||||
// If the network ever for any period of time had satisfied a NetworkRequest (i.e. had been
|
||||
// the highest scoring that satisfied the NetworkRequest's constraints), but is no longer the
|
||||
// highest scoring network for any NetworkRequest, then there will be a 30s pause before
|
||||
// ConnectivityService disconnects the NetworkAgent's AsyncChannel. During this pause the
|
||||
// network is considered "lingering". This pause exists to allow network communication to be
|
||||
// wrapped up rather than abruptly terminated. During this pause if the network begins satisfying
|
||||
// a NetworkRequest, ConnectivityService will cancel the future disconnection of the NetworkAgent's
|
||||
// AsyncChannel, and the network is no longer considered "lingering".
|
||||
public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
||||
public NetworkInfo networkInfo;
|
||||
// This Network object should always be used if possible, so as to encourage reuse of the
|
||||
@ -156,7 +214,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
|
||||
}
|
||||
|
||||
int score = currentScore;
|
||||
if (!everValidated && !pretendValidated) score -= UNVALIDATED_SCORE_PENALTY;
|
||||
// Use NET_CAPABILITY_VALIDATED here instead of lastValidated, this allows
|
||||
// ConnectivityService.updateCapabilities() to compute the old score prior to updating
|
||||
// networkCapabilities (with a potentially different validated state).
|
||||
if (!networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED) && !pretendValidated) {
|
||||
score -= UNVALIDATED_SCORE_PENALTY;
|
||||
}
|
||||
if (score < 0) score = 0;
|
||||
return score;
|
||||
}
|
||||
|
@ -585,9 +585,12 @@ public class NetworkMonitor extends StateMachine {
|
||||
switch (message.what) {
|
||||
case CMD_NETWORK_CONNECTED:
|
||||
log("Unlingered");
|
||||
// Go straight to active as we've already evaluated.
|
||||
transitionTo(mValidatedState);
|
||||
return HANDLED;
|
||||
// If already validated, go straight to validated state.
|
||||
if (mNetworkAgentInfo.lastValidated) {
|
||||
transitionTo(mValidatedState);
|
||||
return HANDLED;
|
||||
}
|
||||
return NOT_HANDLED;
|
||||
case CMD_LINGER_EXPIRED:
|
||||
if (message.arg1 != mLingerToken)
|
||||
return HANDLED;
|
||||
|
@ -151,6 +151,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
||||
private final NetworkInfo mNetworkInfo;
|
||||
private final NetworkCapabilities mNetworkCapabilities;
|
||||
private final Thread mThread;
|
||||
private final ConditionVariable mDisconnected = new ConditionVariable();
|
||||
private int mScore;
|
||||
private NetworkAgent mNetworkAgent;
|
||||
|
||||
@ -177,7 +178,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
||||
mNetworkAgent = new NetworkAgent(Looper.myLooper(), mServiceContext,
|
||||
"Mock" + typeName, mNetworkInfo, mNetworkCapabilities,
|
||||
new LinkProperties(), mScore, new NetworkMisc()) {
|
||||
public void unwanted() {}
|
||||
public void unwanted() { mDisconnected.open(); }
|
||||
};
|
||||
initComplete.open();
|
||||
Looper.loop();
|
||||
@ -197,8 +198,13 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
||||
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
|
||||
}
|
||||
|
||||
public void connectWithoutInternet() {
|
||||
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
|
||||
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transition this NetworkAgent to CONNECTED state.
|
||||
* Transition this NetworkAgent to CONNECTED state with NET_CAPABILITY_INTERNET.
|
||||
* @param validated Indicate if network should pretend to be validated.
|
||||
*/
|
||||
public void connect(boolean validated) {
|
||||
@ -231,8 +237,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
||||
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
|
||||
}
|
||||
|
||||
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
|
||||
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
|
||||
connectWithoutInternet();
|
||||
|
||||
if (validated) {
|
||||
// Wait for network to validate.
|
||||
@ -252,6 +257,10 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
||||
public Network getNetwork() {
|
||||
return new Network(mNetworkAgent.netId);
|
||||
}
|
||||
|
||||
public ConditionVariable getDisconnectedCV() {
|
||||
return mDisconnected;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MockNetworkFactory extends NetworkFactory {
|
||||
@ -575,6 +584,34 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
||||
verifyNoNetwork();
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
public void testUnlingeringDoesNotValidate() throws Exception {
|
||||
// Test bringing up unvalidated cellular.
|
||||
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
|
||||
ConditionVariable cv = waitForConnectivityBroadcasts(1);
|
||||
mCellNetworkAgent.connect(false);
|
||||
waitFor(cv);
|
||||
verifyActiveNetwork(TRANSPORT_CELLULAR);
|
||||
assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
|
||||
NET_CAPABILITY_VALIDATED));
|
||||
// Test bringing up validated WiFi.
|
||||
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
|
||||
cv = waitForConnectivityBroadcasts(2);
|
||||
mWiFiNetworkAgent.connect(true);
|
||||
waitFor(cv);
|
||||
verifyActiveNetwork(TRANSPORT_WIFI);
|
||||
assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
|
||||
NET_CAPABILITY_VALIDATED));
|
||||
// Test WiFi disconnect.
|
||||
cv = waitForConnectivityBroadcasts(2);
|
||||
mWiFiNetworkAgent.disconnect();
|
||||
waitFor(cv);
|
||||
verifyActiveNetwork(TRANSPORT_CELLULAR);
|
||||
// Unlingering a network should not cause it to be marked as validated.
|
||||
assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
|
||||
NET_CAPABILITY_VALIDATED));
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
public void testCellularOutscoresWeakWifi() throws Exception {
|
||||
// Test bringing up validated cellular.
|
||||
@ -603,6 +640,107 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
||||
mWiFiNetworkAgent.disconnect();
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
public void testReapingNetwork() throws Exception {
|
||||
// Test bringing up WiFi without NET_CAPABILITY_INTERNET.
|
||||
// Expect it to be torn down immediately because it satisfies no requests.
|
||||
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
|
||||
ConditionVariable cv = mWiFiNetworkAgent.getDisconnectedCV();
|
||||
mWiFiNetworkAgent.connectWithoutInternet();
|
||||
waitFor(cv);
|
||||
// Test bringing up cellular without NET_CAPABILITY_INTERNET.
|
||||
// Expect it to be torn down immediately because it satisfies no requests.
|
||||
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
|
||||
cv = mCellNetworkAgent.getDisconnectedCV();
|
||||
mCellNetworkAgent.connectWithoutInternet();
|
||||
waitFor(cv);
|
||||
// Test bringing up validated WiFi.
|
||||
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
|
||||
cv = waitForConnectivityBroadcasts(1);
|
||||
mWiFiNetworkAgent.connect(true);
|
||||
waitFor(cv);
|
||||
verifyActiveNetwork(TRANSPORT_WIFI);
|
||||
// Test bringing up unvalidated cellular.
|
||||
// Expect it to be torn down because it could never be the highest scoring network
|
||||
// satisfying the default request even if it validated.
|
||||
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
|
||||
cv = mCellNetworkAgent.getDisconnectedCV();
|
||||
mCellNetworkAgent.connect(false);
|
||||
waitFor(cv);
|
||||
verifyActiveNetwork(TRANSPORT_WIFI);
|
||||
cv = mWiFiNetworkAgent.getDisconnectedCV();
|
||||
mWiFiNetworkAgent.disconnect();
|
||||
waitFor(cv);
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
public void testCellularFallback() throws Exception {
|
||||
// Test bringing up validated cellular.
|
||||
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
|
||||
ConditionVariable cv = waitForConnectivityBroadcasts(1);
|
||||
mCellNetworkAgent.connect(true);
|
||||
waitFor(cv);
|
||||
verifyActiveNetwork(TRANSPORT_CELLULAR);
|
||||
// Test bringing up validated WiFi.
|
||||
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
|
||||
cv = waitForConnectivityBroadcasts(2);
|
||||
mWiFiNetworkAgent.connect(true);
|
||||
waitFor(cv);
|
||||
verifyActiveNetwork(TRANSPORT_WIFI);
|
||||
// Reevaluate WiFi (it'll instantly fail DNS).
|
||||
cv = waitForConnectivityBroadcasts(2);
|
||||
assertTrue(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
|
||||
NET_CAPABILITY_VALIDATED));
|
||||
mCm.reportBadNetwork(mWiFiNetworkAgent.getNetwork());
|
||||
// Should quickly fall back to Cellular.
|
||||
waitFor(cv);
|
||||
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
|
||||
NET_CAPABILITY_VALIDATED));
|
||||
verifyActiveNetwork(TRANSPORT_CELLULAR);
|
||||
// Reevaluate cellular (it'll instantly fail DNS).
|
||||
cv = waitForConnectivityBroadcasts(2);
|
||||
assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
|
||||
NET_CAPABILITY_VALIDATED));
|
||||
mCm.reportBadNetwork(mCellNetworkAgent.getNetwork());
|
||||
// Should quickly fall back to WiFi.
|
||||
waitFor(cv);
|
||||
assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
|
||||
NET_CAPABILITY_VALIDATED));
|
||||
assertFalse(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()).hasCapability(
|
||||
NET_CAPABILITY_VALIDATED));
|
||||
verifyActiveNetwork(TRANSPORT_WIFI);
|
||||
mCellNetworkAgent.disconnect();
|
||||
mWiFiNetworkAgent.disconnect();
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
public void testWiFiFallback() throws Exception {
|
||||
// Test bringing up unvalidated WiFi.
|
||||
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
|
||||
ConditionVariable cv = waitForConnectivityBroadcasts(1);
|
||||
mWiFiNetworkAgent.connect(false);
|
||||
waitFor(cv);
|
||||
verifyActiveNetwork(TRANSPORT_WIFI);
|
||||
// Test bringing up validated cellular.
|
||||
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
|
||||
cv = waitForConnectivityBroadcasts(2);
|
||||
mCellNetworkAgent.connect(true);
|
||||
waitFor(cv);
|
||||
verifyActiveNetwork(TRANSPORT_CELLULAR);
|
||||
// Reevaluate cellular (it'll instantly fail DNS).
|
||||
cv = waitForConnectivityBroadcasts(2);
|
||||
assertTrue(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
|
||||
NET_CAPABILITY_VALIDATED));
|
||||
mCm.reportBadNetwork(mCellNetworkAgent.getNetwork());
|
||||
// Should quickly fall back to WiFi.
|
||||
waitFor(cv);
|
||||
assertFalse(mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork()).hasCapability(
|
||||
NET_CAPABILITY_VALIDATED));
|
||||
verifyActiveNetwork(TRANSPORT_WIFI);
|
||||
mCellNetworkAgent.disconnect();
|
||||
mWiFiNetworkAgent.disconnect();
|
||||
}
|
||||
|
||||
enum CallbackState {
|
||||
NONE,
|
||||
AVAILABLE,
|
||||
@ -872,6 +1010,71 @@ public class ConnectivityServiceTest extends AndroidTestCase {
|
||||
} catch (IllegalArgumentException expected) {}
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
public void testMMSonWiFi() throws Exception {
|
||||
// Test bringing up cellular without MMS NetworkRequest gets reaped
|
||||
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
|
||||
mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
|
||||
ConditionVariable cv = mCellNetworkAgent.getDisconnectedCV();
|
||||
mCellNetworkAgent.connectWithoutInternet();
|
||||
waitFor(cv);
|
||||
waitFor(new Criteria() {
|
||||
public boolean get() { return mCm.getAllNetworks().length == 0; } });
|
||||
verifyNoNetwork();
|
||||
// Test bringing up validated WiFi.
|
||||
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
|
||||
cv = waitForConnectivityBroadcasts(1);
|
||||
mWiFiNetworkAgent.connect(true);
|
||||
waitFor(cv);
|
||||
verifyActiveNetwork(TRANSPORT_WIFI);
|
||||
// Register MMS NetworkRequest
|
||||
NetworkRequest.Builder builder = new NetworkRequest.Builder();
|
||||
builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
|
||||
final TestNetworkCallback networkCallback = new TestNetworkCallback();
|
||||
mCm.requestNetwork(builder.build(), networkCallback);
|
||||
// Test bringing up unvalidated cellular with MMS
|
||||
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
|
||||
mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
|
||||
cv = networkCallback.getConditionVariable();
|
||||
mCellNetworkAgent.connectWithoutInternet();
|
||||
waitFor(cv);
|
||||
assertEquals(CallbackState.AVAILABLE, networkCallback.getLastCallback());
|
||||
verifyActiveNetwork(TRANSPORT_WIFI);
|
||||
// Test releasing NetworkRequest disconnects cellular with MMS
|
||||
cv = mCellNetworkAgent.getDisconnectedCV();
|
||||
mCm.unregisterNetworkCallback(networkCallback);
|
||||
waitFor(cv);
|
||||
verifyActiveNetwork(TRANSPORT_WIFI);
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
public void testMMSonCell() throws Exception {
|
||||
// Test bringing up cellular without MMS
|
||||
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
|
||||
ConditionVariable cv = waitForConnectivityBroadcasts(1);
|
||||
mCellNetworkAgent.connect(false);
|
||||
waitFor(cv);
|
||||
verifyActiveNetwork(TRANSPORT_CELLULAR);
|
||||
// Register MMS NetworkRequest
|
||||
NetworkRequest.Builder builder = new NetworkRequest.Builder();
|
||||
builder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
|
||||
final TestNetworkCallback networkCallback = new TestNetworkCallback();
|
||||
mCm.requestNetwork(builder.build(), networkCallback);
|
||||
// Test bringing up MMS cellular network
|
||||
cv = networkCallback.getConditionVariable();
|
||||
MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
|
||||
mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS);
|
||||
mmsNetworkAgent.connectWithoutInternet();
|
||||
waitFor(cv);
|
||||
assertEquals(CallbackState.AVAILABLE, networkCallback.getLastCallback());
|
||||
verifyActiveNetwork(TRANSPORT_CELLULAR);
|
||||
// Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent
|
||||
cv = mmsNetworkAgent.getDisconnectedCV();
|
||||
mCm.unregisterNetworkCallback(networkCallback);
|
||||
waitFor(cv);
|
||||
verifyActiveNetwork(TRANSPORT_CELLULAR);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void tearDown() throws Exception {
|
||||
// super.tearDown();
|
||||
|
Reference in New Issue
Block a user