diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 5f1043ba8f..f0673fffc0 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -2744,7 +2744,9 @@ public class ConnectivityManager { if (networkCallback == null) { throw new IllegalArgumentException("null NetworkCallback"); } - if (need == null) throw new IllegalArgumentException("null NetworkCapabilities"); + if (need == null && action != REQUEST) { + throw new IllegalArgumentException("null NetworkCapabilities"); + } try { incCallbackHandlerRefCount(); synchronized(sNetworkCallback) { @@ -2767,7 +2769,7 @@ public class ConnectivityManager { } /** - * Helper function to requests a network with a particular legacy type. + * Helper function to request a network with a particular legacy type. * * This is temporarily public @hide so it can be called by system code that uses the * NetworkRequest API to request networks but relies on CONNECTIVITY_ACTION broadcasts for @@ -3010,6 +3012,28 @@ public class ConnectivityManager { } } + /** + * Registers to receive notifications about whichever network currently satisfies the + * system default {@link NetworkRequest}. The callbacks will continue to be called until + * either the application exits or {@link #unregisterNetworkCallback} is called + *
This method requires the caller to hold the permission + * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}. + * + * @param networkCallback The {@link NetworkCallback} that the system will call as the + * system default network changes. + * @hide + */ + public void registerDefaultNetworkCallback(NetworkCallback networkCallback) { + // This works because if the NetworkCapabilities are null, + // ConnectivityService takes them from the default request. + // + // Since the capabilities are exactly the same as the default request's + // capabilities, this request is guaranteed, at all times, to be + // satisfied by the same network, if any, that satisfies the default + // request, i.e., the system default network. + sendRequestForNetwork(null, networkCallback, 0, REQUEST, TYPE_NONE); + } + /** * Requests bandwidth update for a given {@link Network} and returns whether the update request * is accepted by ConnectivityService. Once accepted, ConnectivityService will poll underlying diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index b7fca1a10f..5b01062a35 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -3845,8 +3845,16 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, Messenger messenger, int timeoutMs, IBinder binder, int legacyType) { - networkCapabilities = new NetworkCapabilities(networkCapabilities); - enforceNetworkRequestPermissions(networkCapabilities); + // If the requested networkCapabilities is null, take them instead from + // the default network request. This allows callers to keep track of + // the system default network. + if (networkCapabilities == null) { + networkCapabilities = new NetworkCapabilities(mDefaultRequest.networkCapabilities); + enforceAccessPermission(); + } else { + networkCapabilities = new NetworkCapabilities(networkCapabilities); + enforceNetworkRequestPermissions(networkCapabilities); + } enforceMeteredApnPolicy(networkCapabilities); ensureRequestableCapabilities(networkCapabilities); diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java index 34f2e2e125..8e11511127 100644 --- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java @@ -1008,29 +1008,41 @@ public class ConnectivityServiceTest extends AndroidTestCase { private class TestNetworkCallback extends NetworkCallback { private final ConditionVariable mConditionVariable = new ConditionVariable(); private CallbackState mLastCallback = CallbackState.NONE; + private Network mLastNetwork; public void onAvailable(Network network) { assertEquals(CallbackState.NONE, mLastCallback); mLastCallback = CallbackState.AVAILABLE; + mLastNetwork = network; mConditionVariable.open(); } public void onLosing(Network network, int maxMsToLive) { assertEquals(CallbackState.NONE, mLastCallback); mLastCallback = CallbackState.LOSING; + mLastNetwork = network; mConditionVariable.open(); } public void onLost(Network network) { assertEquals(CallbackState.NONE, mLastCallback); mLastCallback = CallbackState.LOST; + mLastNetwork = network; mConditionVariable.open(); } void expectCallback(CallbackState state) { + expectCallback(state, null); + } + + void expectCallback(CallbackState state, MockNetworkAgent mockAgent) { waitFor(mConditionVariable); assertEquals(state, mLastCallback); + if (mockAgent != null) { + assertEquals(mockAgent.getNetwork(), mLastNetwork); + } mLastCallback = CallbackState.NONE; + mLastNetwork = null; mConditionVariable.close(); } @@ -1389,6 +1401,55 @@ public class ConnectivityServiceTest extends AndroidTestCase { execptionCalled); } + @LargeTest + public void testRegisterDefaultNetworkCallback() throws Exception { + final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback(); + mCm.registerDefaultNetworkCallback(defaultNetworkCallback); + defaultNetworkCallback.assertNoCallback(); + + // Create a TRANSPORT_CELLULAR request to keep the mobile interface up + // whenever Wi-Fi is up. Without this, the mobile network agent is + // reaped before any other activity can take place. + final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); + final NetworkRequest cellRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_CELLULAR).build(); + mCm.requestNetwork(cellRequest, cellNetworkCallback); + cellNetworkCallback.assertNoCallback(); + + // Bring up cell and expect CALLBACK_AVAILABLE. + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + defaultNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + + // Bring up wifi and expect CALLBACK_AVAILABLE. + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(true); + cellNetworkCallback.assertNoCallback(); + defaultNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + + // Bring down cell. Expect no default network callback, since it wasn't the default. + mCellNetworkAgent.disconnect(); + cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); + defaultNetworkCallback.assertNoCallback(); + + // Bring up cell. Expect no default network callback, since it won't be the default. + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + mCellNetworkAgent.connect(true); + cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + defaultNetworkCallback.assertNoCallback(); + + // Bring down wifi. Expect the default network callback to notified of LOST wifi + // followed by AVAILABLE cell. + mWiFiNetworkAgent.disconnect(); + cellNetworkCallback.assertNoCallback(); + defaultNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + defaultNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); + mCellNetworkAgent.disconnect(); + cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); + defaultNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); + } + private static class TestKeepaliveCallback extends PacketKeepaliveCallback { public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };