Merge "Dump the protocol of IPv4 offload rule" into sc-dev
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 94f652d..7b58503 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -6288,7 +6288,8 @@
callingAttributionTag);
if (VDBG) log("pendingListenForNetwork for " + nri);
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
+ mHandler.sendMessage(mHandler.obtainMessage(
+ EVENT_REGISTER_NETWORK_LISTENER_WITH_INTENT, nri));
}
/** Returns the next Network provider ID. */
diff --git a/service/src/com/android/server/connectivity/KeepaliveTracker.java b/service/src/com/android/server/connectivity/KeepaliveTracker.java
index 7d922a4..3b58823 100644
--- a/service/src/com/android/server/connectivity/KeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveTracker.java
@@ -366,12 +366,10 @@
Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
}
}
- // Ignore the case when the network disconnects immediately after stop() has been
- // called and the keepalive code is waiting for the response from the modem. This
- // might happen when the caller listens for a lower-layer network disconnect
- // callback and stop the keepalive at that time. But the stop() races with the
- // stop() generated in ConnectivityService network disconnection code path.
- if (mStartedState == STOPPING && reason == ERROR_INVALID_NETWORK) return;
+ // To prevent races from re-entrance of stop(), return if the state is already stopping.
+ // This might happen if multiple event sources stop keepalive in a short time. Such as
+ // network disconnect after user calls stop(), or tear down socket after binder died.
+ if (mStartedState == STOPPING) return;
// Store the reason of stopping, and report it after the keepalive is fully stopped.
if (mStopReason != ERROR_STOP_REASON_UNINITIALIZED) {
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 9ad9522..be1efb6 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -76,6 +76,7 @@
import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_LOCKDOWN_VPN;
import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_NONE;
import static com.android.testutils.MiscAsserts.assertThrows;
@@ -506,6 +507,7 @@
@Test
public void testGetAllNetworkStateSnapshots()
throws InterruptedException {
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
// Make sure cell is active to retrieve IMSI for verification in later step.
final Network cellNetwork = mCtsNetUtils.connectToCell();
final String subscriberId = getSubscriberIdForCellNetwork(cellNetwork);
@@ -943,8 +945,8 @@
// noticeably flaky.
Thread.sleep(NO_CALLBACK_TIMEOUT_MS);
- // TODO: BUG (b/189868426): this should also apply to listens
- if (!useListen) {
+ // For R- frameworks, listens will receive duplicated callbacks. See b/189868426.
+ if (isAtLeastS() || !useListen) {
assertEquals("PendingIntent should only be received once", 1, receivedCount.get());
}
} finally {
@@ -959,8 +961,8 @@
boolean useListen) {
assertArrayEquals(filed.networkCapabilities.getCapabilities(),
broadcasted.networkCapabilities.getCapabilities());
- // TODO: BUG (b/189868426): this should also apply to listens
- if (useListen) return;
+ // For R- frameworks, listens will receive duplicated callbacks. See b/189868426.
+ if (!isAtLeastS() && useListen) return;
assertArrayEquals(filed.networkCapabilities.getTransportTypes(),
broadcasted.networkCapabilities.getTransportTypes());
}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index f6ea964..2aa95ee 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -5566,6 +5566,8 @@
// the follow-up network disconnection will be processed first.
mWiFiNetworkAgent.setKeepaliveResponseDelay(3 * TIMEOUT_MS);
ka.stop();
+ // Call stop() twice shouldn't result in crash, b/182586681.
+ ka.stop();
// Make sure the stop has been processed. Wait for executor idle is needed to prevent
// flaky since the actual stop call to the service is delegated to executor thread.
@@ -5863,37 +5865,59 @@
@Test
public void testNetworkCallbackMaximum() throws Exception {
final int MAX_REQUESTS = 100;
- final int CALLBACKS = 89;
- final int INTENTS = 11;
+ final int CALLBACKS = 87;
+ final int DIFF_INTENTS = 10;
+ final int SAME_INTENTS = 10;
final int SYSTEM_ONLY_MAX_REQUESTS = 250;
- assertEquals(MAX_REQUESTS, CALLBACKS + INTENTS);
+ // Assert 1 (Default request filed before testing) + CALLBACKS + DIFF_INTENTS +
+ // 1 (same intent) = MAX_REQUESTS - 1, since the capacity is MAX_REQUEST - 1.
+ assertEquals(MAX_REQUESTS - 1, 1 + CALLBACKS + DIFF_INTENTS + 1);
NetworkRequest networkRequest = new NetworkRequest.Builder().build();
ArrayList<Object> registered = new ArrayList<>();
- int j = 0;
- while (j++ < CALLBACKS / 2) {
- NetworkCallback cb = new NetworkCallback();
- mCm.requestNetwork(networkRequest, cb);
+ for (int j = 0; j < CALLBACKS; j++) {
+ final NetworkCallback cb = new NetworkCallback();
+ if (j < CALLBACKS / 2) {
+ mCm.requestNetwork(networkRequest, cb);
+ } else {
+ mCm.registerNetworkCallback(networkRequest, cb);
+ }
registered.add(cb);
}
- while (j++ < CALLBACKS) {
- NetworkCallback cb = new NetworkCallback();
- mCm.registerNetworkCallback(networkRequest, cb);
- registered.add(cb);
+
+ // Since ConnectivityService will de-duplicate the request with the same intent,
+ // register multiple times does not really increase multiple requests.
+ final PendingIntent same_pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
+ new Intent("same"), FLAG_IMMUTABLE);
+ for (int j = 0; j < SAME_INTENTS; j++) {
+ mCm.registerNetworkCallback(networkRequest, same_pi);
+ // Wait for the requests with the same intent to be de-duplicated. Because
+ // ConnectivityService side incrementCountOrThrow in binder, decrementCount in handler
+ // thread, waitForIdle is needed to ensure decrementCount being invoked for same intent
+ // requests before doing further tests.
+ waitForIdle();
}
- j = 0;
- while (j++ < INTENTS / 2) {
- final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
- new Intent("a" + j), FLAG_IMMUTABLE);
- mCm.requestNetwork(networkRequest, pi);
- registered.add(pi);
+ for (int j = 0; j < SAME_INTENTS; j++) {
+ mCm.requestNetwork(networkRequest, same_pi);
+ // Wait for the requests with the same intent to be de-duplicated.
+ // Refer to the reason above.
+ waitForIdle();
}
- while (j++ < INTENTS) {
- final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
- new Intent("b" + j), FLAG_IMMUTABLE);
- mCm.registerNetworkCallback(networkRequest, pi);
- registered.add(pi);
+ registered.add(same_pi);
+
+ for (int j = 0; j < DIFF_INTENTS; j++) {
+ if (j < DIFF_INTENTS / 2) {
+ final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
+ new Intent("a" + j), FLAG_IMMUTABLE);
+ mCm.requestNetwork(networkRequest, pi);
+ registered.add(pi);
+ } else {
+ final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
+ new Intent("b" + j), FLAG_IMMUTABLE);
+ mCm.registerNetworkCallback(networkRequest, pi);
+ registered.add(pi);
+ }
}
// Test that the limit is enforced when MAX_REQUESTS simultaneous requests are added.
@@ -5943,10 +5967,10 @@
for (Object o : registered) {
if (o instanceof NetworkCallback) {
- mCm.unregisterNetworkCallback((NetworkCallback)o);
+ mCm.unregisterNetworkCallback((NetworkCallback) o);
}
if (o instanceof PendingIntent) {
- mCm.unregisterNetworkCallback((PendingIntent)o);
+ mCm.unregisterNetworkCallback((PendingIntent) o);
}
}
waitForIdle();
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 20be5f4..1eac4ea 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -18,10 +18,12 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -40,6 +42,7 @@
import com.android.server.NsdService.DaemonConnection;
import com.android.server.NsdService.DaemonConnectionSupplier;
import com.android.server.NsdService.NativeCallbackReceiver;
+import com.android.testutils.HandlerUtils;
import org.junit.After;
import org.junit.Before;
@@ -48,6 +51,7 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
// TODOs:
// - test client can send requests and receive replies
@@ -58,14 +62,13 @@
static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
private static final long CLEANUP_DELAY_MS = 500;
-
- long mTimeoutMs = 100; // non-final so that tests can adjust the value.
+ private static final long TIMEOUT_MS = 500;
@Mock Context mContext;
@Mock ContentResolver mResolver;
@Mock NsdService.NsdSettings mSettings;
- @Mock DaemonConnection mDaemon;
NativeCallbackReceiver mDaemonCallback;
+ @Spy DaemonConnection mDaemon = new DaemonConnection(mDaemonCallback);
HandlerThread mThread;
TestHandler mHandler;
@@ -74,6 +77,7 @@
MockitoAnnotations.initMocks(this);
mThread = new HandlerThread("mock-service-handler");
mThread.start();
+ doReturn(true).when(mDaemon).execute(any());
mHandler = new TestHandler(mThread.getLooper());
when(mContext.getContentResolver()).thenReturn(mResolver);
}
@@ -95,14 +99,17 @@
// Creating an NsdManager will not cause any cmds executed, which means
// no daemon is started.
NsdManager client1 = connectClient(service);
+ waitForIdle();
verify(mDaemon, never()).execute(any());
// Creating another NsdManager will not cause any cmds executed.
NsdManager client2 = connectClient(service);
+ waitForIdle();
verify(mDaemon, never()).execute(any());
client1.disconnect();
// Still 1 client remains, daemon shouldn't be stopped.
+ waitForIdle();
verify(mDaemon, never()).maybeStop();
client2.disconnect();
@@ -116,11 +123,11 @@
@Test
public void testClientRequestsAreGCedAtDisconnection() {
when(mSettings.isEnabled()).thenReturn(true);
- when(mDaemon.execute(any())).thenReturn(true);
NsdService service = makeService();
NsdManager client = connectClient(service);
+ waitForIdle();
verify(mDaemon, never()).maybeStart();
verify(mDaemon, never()).execute(any());
@@ -130,27 +137,30 @@
// Client registration request
NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
client.registerService(request, PROTOCOL, listener1);
- verify(mDaemon, timeout(mTimeoutMs).times(1)).maybeStart();
- verifyDaemonCommand("register 2 a_name a_type 2201");
+ waitForIdle();
+ verify(mDaemon, times(1)).maybeStart();
+ verifyDaemonCommands("start-service", "register 2 a_name a_type 2201");
// Client discovery request
NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class);
client.discoverServices("a_type", PROTOCOL, listener2);
- verify(mDaemon, timeout(mTimeoutMs).times(1)).maybeStart();
+ waitForIdle();
+ verify(mDaemon, times(1)).maybeStart();
verifyDaemonCommand("discover 3 a_type");
// Client resolve request
NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class);
client.resolveService(request, listener3);
- verify(mDaemon, timeout(mTimeoutMs).times(1)).maybeStart();
+ waitForIdle();
+ verify(mDaemon, times(1)).maybeStart();
verifyDaemonCommand("resolve 4 a_name a_type local.");
// Client disconnects, stop the daemon after CLEANUP_DELAY_MS.
client.disconnect();
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
-
// checks that request are cleaned
- verifyDaemonCommands("stop-register 2", "stop-discover 3", "stop-resolve 4");
+ verifyDaemonCommands("stop-register 2", "stop-discover 3",
+ "stop-resolve 4", "stop-service");
client.disconnect();
}
@@ -158,7 +168,6 @@
@Test
public void testCleanupDelayNoRequestActive() {
when(mSettings.isEnabled()).thenReturn(true);
- when(mDaemon.execute(any())).thenReturn(true);
NsdService service = makeService();
NsdManager client = connectClient(service);
@@ -167,19 +176,25 @@
request.setPort(2201);
NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
client.registerService(request, PROTOCOL, listener1);
- verify(mDaemon, timeout(mTimeoutMs).times(1)).maybeStart();
- verifyDaemonCommand("register 2 a_name a_type 2201");
+ waitForIdle();
+ verify(mDaemon, times(1)).maybeStart();
+ verifyDaemonCommands("start-service", "register 2 a_name a_type 2201");
client.unregisterService(listener1);
verifyDaemonCommand("stop-register 2");
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
+ verifyDaemonCommand("stop-service");
reset(mDaemon);
client.disconnect();
// Client disconnects, after CLEANUP_DELAY_MS, maybeStop the daemon.
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
}
+ private void waitForIdle() {
+ HandlerUtils.waitForIdle(mHandler, TIMEOUT_MS);
+ }
+
NsdService makeService() {
DaemonConnectionSupplier supplier = (callback) -> {
mDaemonCallback = callback;
@@ -196,10 +211,11 @@
}
void verifyDelayMaybeStopDaemon(long cleanupDelayMs) {
+ waitForIdle();
// Stop daemon shouldn't be called immediately.
- verify(mDaemon, timeout(mTimeoutMs).times(0)).maybeStop();
+ verify(mDaemon, never()).maybeStop();
// Clean up the daemon after CLEANUP_DELAY_MS.
- verify(mDaemon, timeout(cleanupDelayMs + mTimeoutMs)).maybeStop();
+ verify(mDaemon, timeout(cleanupDelayMs + TIMEOUT_MS)).maybeStop();
}
void verifyDaemonCommands(String... wants) {
@@ -211,8 +227,9 @@
}
void verifyDaemonCommand(String want, int n) {
- ArgumentCaptor<Object> argumentsCaptor = ArgumentCaptor.forClass(Object.class);
- verify(mDaemon, timeout(mTimeoutMs).times(n)).execute(argumentsCaptor.capture());
+ waitForIdle();
+ final ArgumentCaptor<Object> argumentsCaptor = ArgumentCaptor.forClass(Object.class);
+ verify(mDaemon, times(n)).execute(argumentsCaptor.capture());
String got = "";
for (Object o : argumentsCaptor.getAllValues()) {
got += o + " ";
@@ -220,7 +237,7 @@
assertEquals(want, got.trim());
// rearm deamon for next command verification
reset(mDaemon);
- when(mDaemon.execute(any())).thenReturn(true);
+ doReturn(true).when(mDaemon).execute(any());
}
public static class TestHandler extends Handler {