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:
Paul Jensen
2015-07-10 16:43:55 +00:00
committed by Android Git Automerger
4 changed files with 317 additions and 57 deletions

View File

@ -2064,12 +2064,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
// if it's awaiting captive portal login, or if validation failed), this // if it's awaiting captive portal login, or if validation failed), this
// may trigger a re-evaluation of the network. // may trigger a re-evaluation of the network.
private void unlinger(NetworkAgentInfo nai) { private void unlinger(NetworkAgentInfo nai) {
nai.networkLingered.clear();
if (!nai.lingering) return;
nai.lingering = false; nai.lingering = false;
if (VDBG) log("Canceling linger of " + nai.name()); 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); 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)? // Is nai unneeded by all NetworkRequests (and should be disconnected)?
// For validated Networks this is simply whether it is satsifying any NetworkRequests. // This is whether it is satisfying any NetworkRequests or were it to become validated,
// For unvalidated Networks this is whether it is satsifying any NetworkRequests or // would it have a chance of satisfying any NetworkRequests.
// were it to become validated, would it have a chance of satisfying any NetworkRequests.
private boolean unneeded(NetworkAgentInfo nai) { private boolean unneeded(NetworkAgentInfo nai) {
if (!nai.created || nai.isVPN() || nai.lingering) return false; if (!nai.created || nai.isVPN() || nai.lingering) return false;
boolean unneeded = true; for (NetworkRequestInfo nri : mNetworkRequests.values()) {
if (nai.everValidated) { // If this Network is already the highest scoring Network for a request, or if
for (int i = 0; i < nai.networkRequests.size() && unneeded; i++) { // there is hope for it to become one if it validated, then it is needed.
final NetworkRequest nr = nai.networkRequests.valueAt(i); if (nri.isRequest && nai.satisfies(nri.request) &&
try { (nai.networkRequests.get(nri.request.requestId) != null ||
if (isRequest(nr)) unneeded = false; // Note that this catches two important cases:
} catch (Exception e) { // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
loge("Request " + nr + " not found in mNetworkRequests."); // is currently satisfying the request. This is desirable when
loge(" it came from request list of " + nai.name()); // 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
} else { // WiFi ends up validating and out scoring cellular.
for (NetworkRequestInfo nri : mNetworkRequests.values()) { mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() <
// If this Network is already the highest scoring Network for a request, or if nai.getCurrentScoreAsValidated())) {
// there is hope for it to become one if it validated, then it is needed. return false;
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;
}
} }
} }
return unneeded; return true;
} }
private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) { 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} * augmented with any stateful capabilities implied from {@code networkAgent}
* (e.g., validated status and captive portal status). * (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. * @param networkCapabilities the new network capabilities.
*/ */
private void updateCapabilities(NetworkAgentInfo networkAgent, private void updateCapabilities(NetworkAgentInfo nai, NetworkCapabilities networkCapabilities) {
NetworkCapabilities networkCapabilities) {
// Don't modify caller's NetworkCapabilities. // Don't modify caller's NetworkCapabilities.
networkCapabilities = new NetworkCapabilities(networkCapabilities); networkCapabilities = new NetworkCapabilities(networkCapabilities);
if (networkAgent.lastValidated) { if (nai.lastValidated) {
networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED); networkCapabilities.addCapability(NET_CAPABILITY_VALIDATED);
} else { } else {
networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED); networkCapabilities.removeCapability(NET_CAPABILITY_VALIDATED);
} }
if (networkAgent.lastCaptivePortalDetected) { if (nai.lastCaptivePortalDetected) {
networkCapabilities.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL); networkCapabilities.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
} else { } else {
networkCapabilities.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL); networkCapabilities.removeCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
} }
if (!Objects.equals(networkAgent.networkCapabilities, networkCapabilities)) { if (!Objects.equals(nai.networkCapabilities, networkCapabilities)) {
synchronized (networkAgent) { final int oldScore = nai.getCurrentScore();
networkAgent.networkCapabilities = networkCapabilities; synchronized (nai) {
nai.networkCapabilities = networkCapabilities;
} }
rematchAllNetworksAndRequests(networkAgent, networkAgent.getCurrentScore()); rematchAllNetworksAndRequests(nai, oldScore);
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_CAP_CHANGED); notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
} }
} }
@ -4256,9 +4239,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
// Linger any networks that are no longer needed. // Linger any networks that are no longer needed.
for (NetworkAgentInfo nai : affectedNetworks) { 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); linger(nai);
} else { } else {
// Clear nai.networkLingered we might have added above.
unlinger(nai); unlinger(nai);
} }
} }
@ -4292,7 +4283,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(), mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(),
oldDefaultNetwork, true); oldDefaultNetwork, true);
} }
mDefaultInetConditionPublished = newNetwork.everValidated ? 100 : 0; mDefaultInetConditionPublished = newNetwork.lastValidated ? 100 : 0;
mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork); mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork);
notifyLockdownVpn(newNetwork); notifyLockdownVpn(newNetwork);
} }

View File

@ -16,6 +16,8 @@
package com.android.server.connectivity; package com.android.server.connectivity;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import android.content.Context; import android.content.Context;
import android.net.LinkProperties; import android.net.LinkProperties;
import android.net.Network; import android.net.Network;
@ -39,6 +41,62 @@ import java.util.Comparator;
* AsyncChannel/messenger for reaching that NetworkAgent and lists of NetworkRequests * AsyncChannel/messenger for reaching that NetworkAgent and lists of NetworkRequests
* interested in using it. Default sort order is descending by score. * 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 class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
public NetworkInfo networkInfo; public NetworkInfo networkInfo;
// This Network object should always be used if possible, so as to encourage reuse of the // 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; 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; if (score < 0) score = 0;
return score; return score;
} }

View File

@ -585,9 +585,12 @@ public class NetworkMonitor extends StateMachine {
switch (message.what) { switch (message.what) {
case CMD_NETWORK_CONNECTED: case CMD_NETWORK_CONNECTED:
log("Unlingered"); log("Unlingered");
// Go straight to active as we've already evaluated. // If already validated, go straight to validated state.
transitionTo(mValidatedState); if (mNetworkAgentInfo.lastValidated) {
return HANDLED; transitionTo(mValidatedState);
return HANDLED;
}
return NOT_HANDLED;
case CMD_LINGER_EXPIRED: case CMD_LINGER_EXPIRED:
if (message.arg1 != mLingerToken) if (message.arg1 != mLingerToken)
return HANDLED; return HANDLED;

View File

@ -151,6 +151,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
private final NetworkInfo mNetworkInfo; private final NetworkInfo mNetworkInfo;
private final NetworkCapabilities mNetworkCapabilities; private final NetworkCapabilities mNetworkCapabilities;
private final Thread mThread; private final Thread mThread;
private final ConditionVariable mDisconnected = new ConditionVariable();
private int mScore; private int mScore;
private NetworkAgent mNetworkAgent; private NetworkAgent mNetworkAgent;
@ -177,7 +178,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
mNetworkAgent = new NetworkAgent(Looper.myLooper(), mServiceContext, mNetworkAgent = new NetworkAgent(Looper.myLooper(), mServiceContext,
"Mock" + typeName, mNetworkInfo, mNetworkCapabilities, "Mock" + typeName, mNetworkInfo, mNetworkCapabilities,
new LinkProperties(), mScore, new NetworkMisc()) { new LinkProperties(), mScore, new NetworkMisc()) {
public void unwanted() {} public void unwanted() { mDisconnected.open(); }
}; };
initComplete.open(); initComplete.open();
Looper.loop(); Looper.loop();
@ -197,8 +198,13 @@ public class ConnectivityServiceTest extends AndroidTestCase {
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); 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. * @param validated Indicate if network should pretend to be validated.
*/ */
public void connect(boolean validated) { public void connect(boolean validated) {
@ -231,8 +237,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
} }
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); connectWithoutInternet();
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
if (validated) { if (validated) {
// Wait for network to validate. // Wait for network to validate.
@ -252,6 +257,10 @@ public class ConnectivityServiceTest extends AndroidTestCase {
public Network getNetwork() { public Network getNetwork() {
return new Network(mNetworkAgent.netId); return new Network(mNetworkAgent.netId);
} }
public ConditionVariable getDisconnectedCV() {
return mDisconnected;
}
} }
private static class MockNetworkFactory extends NetworkFactory { private static class MockNetworkFactory extends NetworkFactory {
@ -575,6 +584,34 @@ public class ConnectivityServiceTest extends AndroidTestCase {
verifyNoNetwork(); 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 @LargeTest
public void testCellularOutscoresWeakWifi() throws Exception { public void testCellularOutscoresWeakWifi() throws Exception {
// Test bringing up validated cellular. // Test bringing up validated cellular.
@ -603,6 +640,107 @@ public class ConnectivityServiceTest extends AndroidTestCase {
mWiFiNetworkAgent.disconnect(); 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 { enum CallbackState {
NONE, NONE,
AVAILABLE, AVAILABLE,
@ -872,6 +1010,71 @@ public class ConnectivityServiceTest extends AndroidTestCase {
} catch (IllegalArgumentException expected) {} } 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 // @Override
// public void tearDown() throws Exception { // public void tearDown() throws Exception {
// super.tearDown(); // super.tearDown();