Snap for 7513903 from 5e427a411e73e011ffeb649f3f6ead6e7d5c74d9 to sc-v2-release
Change-Id: Ieebb2802963aa8a948d2b30ca8c1e5850027cd82
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 9b95dac..2c1fd29 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -1069,11 +1069,13 @@
throw new AssertionError("IP address array not valid IPv4 address!");
}
- final long ageMs = (now - value.lastUsed) / 1_000_000;
- return String.format("[%s] %d(%s) %s:%d -> %d(%s) %s:%d -> %s:%d [%s] %dms",
- key.dstMac, key.iif, getIfName(key.iif), src4, key.srcPort,
+ final String protoStr = (key.l4proto == OsConstants.IPPROTO_TCP) ? "tcp" : "udp";
+ final String ageStr = (value.lastUsed == 0) ? "-"
+ : String.format("%dms", (now - value.lastUsed) / 1_000_000);
+ return String.format("%s [%s] %d(%s) %s:%d -> %d(%s) %s:%d -> %s:%d [%s] %s",
+ protoStr, key.dstMac, key.iif, getIfName(key.iif), src4, key.srcPort,
value.oif, getIfName(value.oif),
- public4, publicPort, dst4, value.dstPort, value.ethDstMac, ageMs);
+ public4, publicPort, dst4, value.dstPort, value.ethDstMac, ageStr);
}
private void dumpIpv4ForwardingRuleMap(long now, boolean downstream,
@@ -1094,12 +1096,14 @@
try (BpfMap<Tether4Key, Tether4Value> upstreamMap = mDeps.getBpfUpstream4Map();
BpfMap<Tether4Key, Tether4Value> downstreamMap = mDeps.getBpfDownstream4Map()) {
- pw.println("IPv4 Upstream: [inDstMac] iif(iface) src -> nat -> dst [outDstMac] age");
+ pw.println("IPv4 Upstream: proto [inDstMac] iif(iface) src -> nat -> "
+ + "dst [outDstMac] age");
pw.increaseIndent();
dumpIpv4ForwardingRuleMap(now, UPSTREAM, upstreamMap, pw);
pw.decreaseIndent();
- pw.println("IPv4 Downstream: [inDstMac] iif(iface) src -> nat -> dst [outDstMac] age");
+ pw.println("IPv4 Downstream: proto [inDstMac] iif(iface) src -> nat -> "
+ + "dst [outDstMac] age");
pw.increaseIndent();
dumpIpv4ForwardingRuleMap(now, DOWNSTREAM, downstreamMap, pw);
pw.decreaseIndent();
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index a381621..e91ae85 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -1039,7 +1039,7 @@
final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false);
final boolean ncmEnabled = intent.getBooleanExtra(USB_FUNCTION_NCM, false);
- mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s ncm:%s",
+ mLog.i(String.format("USB bcast connected:%s configured:%s rndis:%s ncm:%s",
usbConnected, usbConfigured, rndisEnabled, ncmEnabled));
// There are three types of ACTION_USB_STATE:
@@ -1416,7 +1416,7 @@
// If TETHERING_USB is forced to use ncm function, TETHERING_NCM would no longer be
// available.
- if (mConfig.isUsingNcm()) return TETHER_ERROR_SERVICE_UNAVAIL;
+ if (mConfig.isUsingNcm() && enable) return TETHER_ERROR_SERVICE_UNAVAIL;
UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_NCM : UsbManager.FUNCTION_NONE);
@@ -2552,7 +2552,7 @@
return;
}
- mLog.log("adding IpServer for: " + iface);
+ mLog.i("adding IpServer for: " + iface);
final TetherState tetherState = new TetherState(
new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator,
makeControlCallback(), mConfig.enableLegacyDhcpServer,
@@ -2567,7 +2567,7 @@
if (tetherState == null) return;
tetherState.ipServer.stop();
- mLog.log("removing IpServer for: " + iface);
+ mLog.i("removing IpServer for: " + iface);
mTetherStates.remove(iface);
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 31fcea4..d2f44d3 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -176,7 +176,9 @@
// us an interface name. Careful consideration needs to be given to
// implications for Settings and for provisioning checks.
tetherableWifiRegexs = getResourceStringArray(res, R.array.config_tether_wifi_regexs);
- tetherableWigigRegexs = getResourceStringArray(res, R.array.config_tether_wigig_regexs);
+ // TODO: Remove entire wigig code once tethering module no longer support R devices.
+ tetherableWigigRegexs = SdkLevel.isAtLeastS()
+ ? new String[0] : getResourceStringArray(res, R.array.config_tether_wigig_regexs);
tetherableWifiP2pRegexs = getResourceStringArray(
res, R.array.config_tether_wifi_p2p_regexs);
tetherableBluetoothRegexs = getResourceStringArray(
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index af28dd7..9e0c880 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -2610,12 +2610,57 @@
reset(mBluetoothAdapter, mBluetoothPan);
}
+ private void runDualStackUsbTethering(final String expectedIface) throws Exception {
+ when(mNetd.interfaceGetList()).thenReturn(new String[] {expectedIface});
+ when(mRouterAdvertisementDaemon.start())
+ .thenReturn(true);
+ final UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
+ runUsbTethering(upstreamState);
+
+ verify(mNetd).interfaceGetList();
+ verify(mNetd).tetherAddForward(expectedIface, TEST_MOBILE_IFNAME);
+ verify(mNetd).ipfwdAddInterfaceForward(expectedIface, TEST_MOBILE_IFNAME);
+
+ verify(mRouterAdvertisementDaemon).start();
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks(
+ any(), any());
+ sendIPv6TetherUpdates(upstreamState);
+ assertSetIfaceToDadProxy(1 /* numOfCalls */, TEST_MOBILE_IFNAME /* ifaceName */);
+ verify(mRouterAdvertisementDaemon).buildNewRa(any(), notNull());
+ verify(mNetd).tetherApplyDnsInterfaces();
+ }
+
+ private void forceUsbTetheringUse(final int function) {
+ Settings.Global.putInt(mContentResolver, TETHER_FORCE_USB_FUNCTIONS, function);
+ final ContentObserver observer = mTethering.getSettingsObserverForTest();
+ observer.onChange(false /* selfChange */);
+ mLooper.dispatchAll();
+ }
+
+ private void verifyUsbTetheringStopDueToSettingChange(final String iface) {
+ verify(mUsbManager, times(2)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+ mTethering.interfaceRemoved(iface);
+ sendUsbBroadcast(true, true, -1 /* no functions enabled */);
+ reset(mUsbManager, mNetd, mDhcpServer, mRouterAdvertisementDaemon,
+ mIPv6TetheringCoordinator, mDadProxy);
+ }
+
@Test
- public void testUsbTetheringWithNcmFunction() throws Exception {
- when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
- TetheringConfiguration.TETHER_USB_NCM_FUNCTION);
+ public void testUsbFunctionConfigurationChange() throws Exception {
+ // Run TETHERING_NCM.
+ runNcmTethering();
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+ any(), any());
+
+ // Change the USB tethering function to NCM. Because the USB tethering function was set to
+ // RNDIS (the default), tethering is stopped.
+ forceUsbTetheringUse(TETHER_USB_NCM_FUNCTION);
+ verifyUsbTetheringStopDueToSettingChange(TEST_NCM_IFNAME);
+
+ // TODO: move this into setup after allowing configure TEST_NCM_REGEX into
+ // config_tether_usb_regexs and config_tether_ncm_regexs at the same time.
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
- .thenReturn(new String[] {TEST_NCM_REGEX});
+ .thenReturn(new String[] {TEST_RNDIS_REGEX, TEST_NCM_REGEX});
sendConfigurationChanged();
// If TETHERING_USB is forced to use ncm function, TETHERING_NCM would no longer be
@@ -2625,30 +2670,16 @@
mLooper.dispatchAll();
ncmResult.assertHasResult();
- final UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
- runUsbTethering(upstreamState);
+ // Run TETHERING_USB with ncm configuration.
+ runDualStackUsbTethering(TEST_NCM_IFNAME);
- verify(mNetd).interfaceGetList();
- verify(mNetd).tetherAddForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
- verify(mNetd).ipfwdAddInterfaceForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
+ // Change configuration to rndis.
+ forceUsbTetheringUse(TETHER_USB_RNDIS_FUNCTION);
+ verifyUsbTetheringStopDueToSettingChange(TEST_NCM_IFNAME);
- verify(mRouterAdvertisementDaemon).start();
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks(
- any(), any());
- sendIPv6TetherUpdates(upstreamState);
- assertSetIfaceToDadProxy(1 /* numOfCalls */, TEST_MOBILE_IFNAME /* ifaceName */);
- verify(mRouterAdvertisementDaemon).buildNewRa(any(), notNull());
- verify(mNetd).tetherApplyDnsInterfaces();
-
- Settings.Global.putInt(mContentResolver, TETHER_FORCE_USB_FUNCTIONS,
- TETHER_USB_RNDIS_FUNCTION);
- final ContentObserver observer = mTethering.getSettingsObserverForTest();
- observer.onChange(false /* selfChange */);
- mLooper.dispatchAll();
- // stop TETHERING_USB and TETHERING_NCM
- verify(mUsbManager, times(2)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
- mTethering.interfaceRemoved(TEST_NCM_IFNAME);
- sendUsbBroadcast(true, true, -1 /* function */);
+ // Run TETHERING_USB with rndis configuration.
+ runDualStackUsbTethering(TEST_RNDIS_IFNAME);
+ runStopUSBTethering();
}
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
diff --git a/framework/src/android/net/util/MultinetworkPolicyTracker.java b/framework/src/android/net/util/MultinetworkPolicyTracker.java
index 7e62d28..9791cbf 100644
--- a/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ b/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -182,7 +182,7 @@
public void setTestAllowBadWifiUntil(long timeMs) {
Log.d(TAG, "setTestAllowBadWifiUntil: " + mTestAllowBadWifiUntilMs);
mTestAllowBadWifiUntilMs = timeMs;
- updateAvoidBadWifi();
+ reevaluateInternal();
}
@VisibleForTesting
diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml
index 70ddb9a..bf32ad5 100644
--- a/service/ServiceConnectivityResources/res/values/config.xml
+++ b/service/ServiceConnectivityResources/res/values/config.xml
@@ -111,4 +111,7 @@
notification that can be dismissed. -->
<bool name="config_ongoingSignInNotification">false</bool>
+ <!-- Whether to cancel network notifications automatically when tapped -->
+ <bool name="config_autoCancelNetworkNotifications">true</bool>
+
</resources>
diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml
index fd23566..6ac6a0e 100644
--- a/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -31,7 +31,9 @@
<item type="integer" name="config_networkNotifySwitchType"/>
<item type="array" name="config_networkNotifySwitches"/>
<item type="bool" name="config_ongoingSignInNotification"/>
-
+ <item type="bool" name="config_autoCancelNetworkNotifications"/>
+ <item type="drawable" name="stat_notify_wifi_in_range"/>
+ <item type="drawable" name="stat_notify_rssi_in_range"/>
</policy>
</overlayable>
</resources>
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/service/src/com/android/server/connectivity/NetworkNotificationManager.java b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
index 3dc79c5..ae98d92 100644
--- a/service/src/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
@@ -274,7 +274,7 @@
.setWhen(System.currentTimeMillis())
.setShowWhen(notifyType == NotificationType.NETWORK_SWITCH)
.setSmallIcon(icon)
- .setAutoCancel(true)
+ .setAutoCancel(r.getBoolean(R.bool.config_autoCancelNetworkNotifications))
.setTicker(title)
.setColor(mContext.getColor(android.R.color.system_notification_accent_color))
.setContentTitle(title)
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 {
diff --git a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index c353cea..115efbb 100644
--- a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity;
+import static android.app.Notification.FLAG_AUTO_CANCEL;
import static android.app.Notification.FLAG_ONGOING_EVENT;
import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.LOST_INTERNET;
@@ -78,6 +79,8 @@
private static final String TEST_SSID = "Test SSID";
private static final String TEST_EXTRA_INFO = "extra";
+ private static final int TEST_NOTIF_ID = 101;
+ private static final String TEST_NOTIF_TAG = NetworkNotificationManager.tagFor(TEST_NOTIF_ID);
static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities();
static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities();
static final NetworkCapabilities VPN_CAPABILITIES = new NetworkCapabilities();
@@ -142,6 +145,7 @@
}
when(mResources.getStringArray(R.array.network_switch_type_name))
.thenReturn(transportNames);
+ when(mResources.getBoolean(R.bool.config_autoCancelNetworkNotifications)).thenReturn(true);
mManager = new NetworkNotificationManager(mCtx, mTelephonyManager);
}
@@ -238,57 +242,65 @@
verify(mNotificationManager, never()).notify(any(), anyInt(), any());
}
- private void assertNotification(NotificationType type, boolean ongoing) {
- final int id = 101;
- final String tag = NetworkNotificationManager.tagFor(id);
+ private void assertNotification(NotificationType type, boolean ongoing, boolean autoCancel) {
final ArgumentCaptor<Notification> noteCaptor = ArgumentCaptor.forClass(Notification.class);
- mManager.showNotification(id, type, mWifiNai, mCellNai, null, false);
- verify(mNotificationManager, times(1)).notify(eq(tag), eq(type.eventId),
+ mManager.showNotification(TEST_NOTIF_ID, type, mWifiNai, mCellNai, null, false);
+ verify(mNotificationManager, times(1)).notify(eq(TEST_NOTIF_TAG), eq(type.eventId),
noteCaptor.capture());
assertEquals("Notification ongoing flag should be " + (ongoing ? "set" : "unset"),
ongoing, (noteCaptor.getValue().flags & FLAG_ONGOING_EVENT) != 0);
+ assertEquals("Notification autocancel flag should be " + (autoCancel ? "set" : "unset"),
+ autoCancel, (noteCaptor.getValue().flags & FLAG_AUTO_CANCEL) != 0);
}
@Test
public void testDuplicatedNotificationsNoInternetThenSignIn() {
- final int id = 101;
- final String tag = NetworkNotificationManager.tagFor(id);
-
// Show first NO_INTERNET
- assertNotification(NO_INTERNET, false /* ongoing */);
+ assertNotification(NO_INTERNET, false /* ongoing */, true /* autoCancel */);
// Captive portal detection triggers SIGN_IN a bit later, clearing the previous NO_INTERNET
- assertNotification(SIGN_IN, false /* ongoing */);
- verify(mNotificationManager, times(1)).cancel(eq(tag), eq(NO_INTERNET.eventId));
+ assertNotification(SIGN_IN, false /* ongoing */, true /* autoCancel */);
+ verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(NO_INTERNET.eventId));
// Network disconnects
- mManager.clearNotification(id);
- verify(mNotificationManager, times(1)).cancel(eq(tag), eq(SIGN_IN.eventId));
+ mManager.clearNotification(TEST_NOTIF_ID);
+ verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(SIGN_IN.eventId));
}
@Test
public void testOngoingSignInNotification() {
doReturn(true).when(mResources).getBoolean(R.bool.config_ongoingSignInNotification);
- final int id = 101;
- final String tag = NetworkNotificationManager.tagFor(id);
// Show first NO_INTERNET
- assertNotification(NO_INTERNET, false /* ongoing */);
+ assertNotification(NO_INTERNET, false /* ongoing */, true /* autoCancel */);
// Captive portal detection triggers SIGN_IN a bit later, clearing the previous NO_INTERNET
- assertNotification(SIGN_IN, true /* ongoing */);
- verify(mNotificationManager, times(1)).cancel(eq(tag), eq(NO_INTERNET.eventId));
+ assertNotification(SIGN_IN, true /* ongoing */, true /* autoCancel */);
+ verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(NO_INTERNET.eventId));
// Network disconnects
- mManager.clearNotification(id);
- verify(mNotificationManager, times(1)).cancel(eq(tag), eq(SIGN_IN.eventId));
+ mManager.clearNotification(TEST_NOTIF_ID);
+ verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(SIGN_IN.eventId));
+ }
+
+ @Test
+ public void testNoAutoCancelNotification() {
+ doReturn(false).when(mResources).getBoolean(R.bool.config_autoCancelNetworkNotifications);
+
+ // Show NO_INTERNET, then SIGN_IN
+ assertNotification(NO_INTERNET, false /* ongoing */, false /* autoCancel */);
+ assertNotification(SIGN_IN, false /* ongoing */, false /* autoCancel */);
+ verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(NO_INTERNET.eventId));
+
+ mManager.clearNotification(TEST_NOTIF_ID);
+ verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(SIGN_IN.eventId));
}
@Test
public void testDuplicatedNotificationsSignInThenNoInternet() {
- final int id = 101;
- final String tag = NetworkNotificationManager.tagFor(id);
+ final int id = TEST_NOTIF_ID;
+ final String tag = TEST_NOTIF_TAG;
// Show first SIGN_IN
mManager.showNotification(id, SIGN_IN, mWifiNai, mCellNai, null, false);
@@ -307,8 +319,8 @@
@Test
public void testClearNotificationByType() {
- final int id = 101;
- final String tag = NetworkNotificationManager.tagFor(id);
+ final int id = TEST_NOTIF_ID;
+ final String tag = TEST_NOTIF_TAG;
// clearNotification(int id, NotificationType notifyType) will check if given type is equal
// to previous type or not. If they are equal then clear the notification; if they are not