From 1f5ffa4273a98de4e9958adb9483fe48a226c811 Mon Sep 17 00:00:00 2001 From: Motomu Utsumi Date: Mon, 29 May 2023 16:07:19 +0900 Subject: [PATCH] Track multiple network activities on V+ on V+ devices, NetworkActivityTracker adds idleTimer when the network is first connected and removes idleTimer when the network is disconnected so that activity tracker can receive netd onInterfaceClassActivityChanged callback for multiple networks. Bug: 267870186 Bug: 279380356 Test: atest FrameworksNetTests Change-Id: I6f3a95c27e3e58f3f60c40065f562d00431a56b1 --- .../android/server/ConnectivityService.java | 55 +++-- .../server/ConnectivityServiceTest.java | 72 ++++-- .../CSNetworkActivityTest.kt | 215 ++++++++++++++++++ .../server/connectivityservice/base/CSTest.kt | 24 ++ 4 files changed, 334 insertions(+), 32 deletions(-) create mode 100644 tests/unit/java/com/android/server/connectivityservice/CSNetworkActivityTest.kt diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index 28f104e446..a9af7b4878 100755 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -4960,6 +4960,11 @@ public class ConnectivityService extends IConnectivityManager.Stub if (wasDefault) { mDefaultInetConditionPublished = 0; } + if (mTrackMultiNetworkActivities) { + // If trackMultiNetworkActivities is disabled, ActivityTracker removes idleTimer when + // the network becomes no longer the default network. + mNetworkActivityTracker.removeDataActivityTracking(nai); + } notifyIfacesChangedForNetworkStats(); // If this was a local network forwarded to some upstream, or if some local network was // forwarded to this nai, then disable forwarding rules now. @@ -5013,12 +5018,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } if (mDefaultRequest == nri) { - // TODO : make battery stats aware that since 2013 multiple interfaces may be - // active at the same time. For now keep calling this with the default - // network, because while incorrect this is the closest to the old (also - // incorrect) behavior. - mNetworkActivityTracker.updateDataActivityTracking( - null /* newNetwork */, nai); + mNetworkActivityTracker.updateDefaultNetwork(null /* newNetwork */, nai); maybeClosePendingFrozenSockets(null /* newNetwork */, nai); ensureNetworkTransitionWakelock(nai.toShortString()); } @@ -9639,7 +9639,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (oldDefaultNetwork != null) { mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork); } - mNetworkActivityTracker.updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork); + mNetworkActivityTracker.updateDefaultNetwork(newDefaultNetwork, oldDefaultNetwork); maybeClosePendingFrozenSockets(newDefaultNetwork, oldDefaultNetwork); mProxyTracker.setDefaultProxy(null != newDefaultNetwork ? newDefaultNetwork.linkProperties.getHttpProxy() : null); @@ -10539,6 +10539,15 @@ public class ConnectivityService extends IConnectivityManager.Stub SystemClock.elapsedRealtime(), mNascentDelayMs); networkAgent.setInactive(); + if (mTrackMultiNetworkActivities) { + // Start tracking activity of this network. + // This must be called before rematchAllNetworksAndRequests since the network + // should be tracked when the network becomes the default network. + // This method does not trigger any callbacks or broadcasts. Callbacks or broadcasts + // can be triggered later if this network becomes the default network. + mNetworkActivityTracker.setupDataActivityTracking(networkAgent); + } + // Consider network even though it is not yet validated. rematchAllNetworksAndRequests(); @@ -11944,13 +11953,17 @@ public class ConnectivityService extends IConnectivityManager.Stub * @return true if the idleTimer is added to the network, false otherwise */ private boolean setupDataActivityTracking(NetworkAgentInfo networkAgent) { + ensureRunningOnConnectivityServiceThread(); final String iface = networkAgent.linkProperties.getInterfaceName(); final int netId = networkAgent.network().netId; final int timeout; final int type; - if (networkAgent.networkCapabilities.hasTransport( + if (!networkAgent.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_VPN)) { + // Do not track VPN network. + return false; + } else if (networkAgent.networkCapabilities.hasTransport( NetworkCapabilities.TRANSPORT_CELLULAR)) { timeout = Settings.Global.getInt(mContext.getContentResolver(), ConnectivitySettingsManager.DATA_ACTIVITY_TIMEOUT_MOBILE, @@ -11986,7 +11999,8 @@ public class ConnectivityService extends IConnectivityManager.Stub /** * Remove data activity tracking when network disconnects. */ - private void removeDataActivityTracking(NetworkAgentInfo networkAgent) { + public void removeDataActivityTracking(NetworkAgentInfo networkAgent) { + ensureRunningOnConnectivityServiceThread(); final String iface = networkAgent.linkProperties.getInterfaceName(); final int netId = networkAgent.network().netId; final NetworkCapabilities caps = networkAgent.networkCapabilities; @@ -11994,7 +12008,10 @@ public class ConnectivityService extends IConnectivityManager.Stub if (iface == null) return; final int type; - if (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + if (!networkAgent.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_VPN)) { + // Do not track VPN network. + return; + } else if (caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { type = NetworkCapabilities.TRANSPORT_CELLULAR; } else if (caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { type = NetworkCapabilities.TRANSPORT_WIFI; @@ -12025,8 +12042,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (defaultNetwork != null) { mDefaultNetwork = defaultNetwork.network(); mIsDefaultNetworkActive = true; - // Callbacks are called only when the network has the idle timer. - if (hasIdleTimer) { + // If only the default network is tracked, callbacks are called only when the + // network has the idle timer. + if (mTrackMultiNetworkActivities || hasIdleTimer) { reportNetworkActive(); } } else { @@ -12038,17 +12056,22 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** - * Update data activity tracking when network state is updated. + * Update the default network this class tracks the activity of. */ - public void updateDataActivityTracking(NetworkAgentInfo newNetwork, + public void updateDefaultNetwork(NetworkAgentInfo newNetwork, NetworkAgentInfo oldNetwork) { ensureRunningOnConnectivityServiceThread(); + // If TrackMultiNetworkActivities is enabled, devices add idleTimer when the network is + // first connected and remove when the network is disconnected. + // If TrackMultiNetworkActivities is disabled, devices add idleTimer when the network + // becomes the default network and remove when the network becomes no longer the default + // network. boolean hasIdleTimer = false; - if (newNetwork != null) { + if (!mTrackMultiNetworkActivities && newNetwork != null) { hasIdleTimer = setupDataActivityTracking(newNetwork); } updateDefaultNetworkActivity(newNetwork, hasIdleTimer); - if (oldNetwork != null) { + if (!mTrackMultiNetworkActivities && oldNetwork != null) { removeDataActivityTracking(oldNetwork); } } diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java index 01fc3b55db..67aac9fdef 100755 --- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java @@ -11426,12 +11426,13 @@ public class ConnectivityServiceTest { } @Test - public void testOnNetworkActive_NewEthernetConnects_CallbackNotCalled() throws Exception { - // LegacyNetworkActivityTracker calls onNetworkActive callback only for networks that - // tracker adds the idle timer to. And the tracker does not set the idle timer for the - // ethernet network. + public void testOnNetworkActive_NewEthernetConnects_Callback() throws Exception { + // On pre-V devices, LegacyNetworkActivityTracker calls onNetworkActive callback only for + // networks that tracker adds the idle timer to. And the tracker does not set the idle timer + // for the ethernet network. // So onNetworkActive is not called when the ethernet becomes the default network - doTestOnNetworkActive_NewNetworkConnects(TRANSPORT_ETHERNET, false /* expectCallback */); + final boolean expectCallback = mDeps.isAtLeastV(); + doTestOnNetworkActive_NewNetworkConnects(TRANSPORT_ETHERNET, expectCallback); } @Test @@ -11485,8 +11486,17 @@ public class ConnectivityServiceTest { networkCallback.expectCaps(mWiFiAgent, c -> c.hasCapability(NET_CAPABILITY_VALIDATED)); verify(mMockNetd, times(1)).idletimerAddInterface(eq(WIFI_IFNAME), anyInt(), eq(wifiIdleTimerLabel)); - verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), - eq(cellIdleTimerLabel)); + if (mDeps.isAtLeastV()) { + // V+ devices add idleTimer when the network is first connected and remove when the + // network is disconnected. + verify(mMockNetd, never()).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(mCellAgent.getNetwork().netId))); + } else { + // pre V devices add idleTimer when the network becomes the default network and remove + // when the network becomes no longer the default network. + verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_CELLULAR))); + } // Disconnect wifi and switch back to cell reset(mMockNetd); @@ -11495,8 +11505,13 @@ public class ConnectivityServiceTest { assertNoCallbacks(networkCallback); verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(WIFI_IFNAME), anyInt(), eq(wifiIdleTimerLabel)); - verify(mMockNetd, times(1)).idletimerAddInterface(eq(MOBILE_IFNAME), anyInt(), - eq(cellIdleTimerLabel)); + if (mDeps.isAtLeastV()) { + verify(mMockNetd, never()).idletimerAddInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(mCellAgent.getNetwork().netId))); + } else { + verify(mMockNetd, times(1)).idletimerAddInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_CELLULAR))); + } // reconnect wifi reset(mMockNetd); @@ -11511,19 +11526,29 @@ public class ConnectivityServiceTest { networkCallback.expectCaps(mWiFiAgent, c -> c.hasCapability(NET_CAPABILITY_VALIDATED)); verify(mMockNetd, times(1)).idletimerAddInterface(eq(WIFI_IFNAME), anyInt(), eq(wifiIdleTimerLabel)); - verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), - eq(cellIdleTimerLabel)); + if (mDeps.isAtLeastV()) { + verify(mMockNetd, never()).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(mCellAgent.getNetwork().netId))); + } else { + verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_CELLULAR))); + } // Disconnect cell reset(mMockNetd); mCellAgent.disconnect(); networkCallback.expect(LOST, mCellAgent); - // LOST callback is triggered earlier than removing idle timer. Broadcast should also be - // sent as network being switched. Ensure rule removal for cell will not be triggered - // unexpectedly before network being removed. waitForIdle(); - verify(mMockNetd, times(0)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), - eq(cellIdleTimerLabel)); + if (mDeps.isAtLeastV()) { + verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(mCellAgent.getNetwork().netId))); + } else { + // LOST callback is triggered earlier than removing idle timer. Broadcast should also be + // sent as network being switched. Ensure rule removal for cell will not be triggered + // unexpectedly before network being removed. + verify(mMockNetd, times(0)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(), + eq(Integer.toString(TRANSPORT_CELLULAR))); + } verify(mMockNetd, times(1)).networkDestroy(eq(mCellAgent.getNetwork().netId)); verify(mMockDnsResolver, times(1)).destroyNetworkCache(eq(mCellAgent.getNetwork().netId)); @@ -11538,6 +11563,21 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(networkCallback); } + @Test + public void testDataActivityTracking_VpnNetwork() throws Exception { + mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mWiFiAgent.connect(true /* validated */); + mMockVpn.setUnderlyingNetworks(new Network[] { mWiFiAgent.getNetwork() }); + + final LinkProperties lp = new LinkProperties(); + lp.setInterfaceName(VPN_IFNAME); + mMockVpn.establishForMyUid(lp); + + // NetworkActivityTracker should not track the VPN network since VPN can change the + // underlying network without disconnect. + verify(mMockNetd, never()).idletimerAddInterface(eq(VPN_IFNAME), anyInt(), any()); + } + private void verifyTcpBufferSizeChange(String tcpBufferSizes) throws Exception { String[] values = tcpBufferSizes.split(","); String rmemValues = String.join(" ", values[0], values[1], values[2]); diff --git a/tests/unit/java/com/android/server/connectivityservice/CSNetworkActivityTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSNetworkActivityTest.kt new file mode 100644 index 0000000000..1f3ba44f78 --- /dev/null +++ b/tests/unit/java/com/android/server/connectivityservice/CSNetworkActivityTest.kt @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server + +import android.net.ConnectivityManager +import android.net.ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE +import android.net.ConnectivityManager.EXTRA_DEVICE_TYPE +import android.net.ConnectivityManager.EXTRA_IS_ACTIVE +import android.net.ConnectivityManager.EXTRA_REALTIME_NS +import android.net.LinkProperties +import android.net.NetworkCapabilities +import android.net.NetworkCapabilities.NET_CAPABILITY_IMS +import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET +import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED +import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED +import android.net.NetworkCapabilities.TRANSPORT_CELLULAR +import android.net.NetworkCapabilities.TRANSPORT_WIFI +import android.net.NetworkRequest +import android.os.Build +import android.os.ConditionVariable +import androidx.test.filters.SmallTest +import com.android.net.module.util.BaseNetdUnsolicitedEventListener +import com.android.server.CSTest.CSContext +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo +import com.android.testutils.DevSdkIgnoreRunner +import com.android.testutils.RecorderCallback.CallbackEntry.Lost +import com.android.testutils.TestableNetworkCallback +import kotlin.test.assertNotNull +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.never +import org.mockito.Mockito.verify + +private const val DATA_CELL_IFNAME = "rmnet_data" +private const val IMS_CELL_IFNAME = "rmnet_ims" +private const val WIFI_IFNAME = "wlan0" +private const val TIMESTAMP = 1234L +private const val NETWORK_ACTIVITY_NO_UID = -1 +private const val PACKAGE_UID = 123 +private const val CALLBACK_TIMEOUT_MS = 250L + +@RunWith(DevSdkIgnoreRunner::class) +@SmallTest +@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) +class CSNetworkActivityTest : CSTest() { + + private fun getRegisteredNetdUnsolicitedEventListener(): BaseNetdUnsolicitedEventListener { + val captor = ArgumentCaptor.forClass(BaseNetdUnsolicitedEventListener::class.java) + verify(netd).registerUnsolicitedEventListener(captor.capture()) + return captor.value + } + + @Test + fun testInterfaceClassActivityChanged_NonDefaultNetwork() { + val netdUnsolicitedEventListener = getRegisteredNetdUnsolicitedEventListener() + + val cellNr = NetworkRequest.Builder() + .clearCapabilities() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) + .build() + val cellCb = TestableNetworkCallback() + // Request cell network to keep cell network up + cm.requestNetwork(cellNr, cellCb) + + val defaultCb = TestableNetworkCallback() + cm.registerDefaultNetworkCallback(defaultCb) + + val cellNc = NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_SUSPENDED) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .build() + val cellLp = LinkProperties().apply { + interfaceName = DATA_CELL_IFNAME + } + // Connect Cellular network + val cellAgent = Agent(nc = cellNc, lp = cellLp) + cellAgent.connect() + defaultCb.expectAvailableCallbacks(cellAgent.network, validated = false) + + val wifiNc = NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_WIFI) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .build() + val wifiLp = LinkProperties().apply { + interfaceName = WIFI_IFNAME + } + // Connect Wi-Fi network, Wi-Fi network should be the default network. + val wifiAgent = Agent(nc = wifiNc, lp = wifiLp) + wifiAgent.connect() + defaultCb.expectAvailableCallbacks(wifiAgent.network, validated = false) + + val onNetworkActiveCv = ConditionVariable() + val listener = ConnectivityManager.OnNetworkActiveListener { onNetworkActiveCv::open } + cm.addDefaultNetworkActiveListener(listener) + + // Cellular network (non default network) goes to inactive state. + netdUnsolicitedEventListener.onInterfaceClassActivityChanged(false /* isActive */, + cellAgent.network.netId, TIMESTAMP, NETWORK_ACTIVITY_NO_UID) + // Non-default network activity change does not change default network activity + assertFalse(onNetworkActiveCv.block(CALLBACK_TIMEOUT_MS)) + context.expectNoDataActivityBroadcast(0 /* timeoutMs */) + assertTrue(cm.isDefaultNetworkActive) + + // Cellular network (non default network) goes to active state. + netdUnsolicitedEventListener.onInterfaceClassActivityChanged(true /* isActive */, + cellAgent.network.netId, TIMESTAMP, PACKAGE_UID) + // Non-default network activity change does not change default network activity + assertFalse(onNetworkActiveCv.block(CALLBACK_TIMEOUT_MS)) + context.expectNoDataActivityBroadcast(0 /* timeoutMs */) + assertTrue(cm.isDefaultNetworkActive) + + cm.unregisterNetworkCallback(cellCb) + cm.unregisterNetworkCallback(defaultCb) + cm.removeDefaultNetworkActiveListener(listener) + } + + @Test + fun testDataActivityTracking_MultiCellNetwork() { + val dataNetworkNc = NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_SUSPENDED) + .build() + val dataNetworkNr = NetworkRequest.Builder() + .clearCapabilities() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) + .build() + val dataNetworkLp = LinkProperties().apply { + interfaceName = DATA_CELL_IFNAME + } + val dataNetworkCb = TestableNetworkCallback() + cm.requestNetwork(dataNetworkNr, dataNetworkCb) + val dataNetworkAgent = Agent(nc = dataNetworkNc, lp = dataNetworkLp) + val dataNetworkNetId = dataNetworkAgent.network.netId.toString() + + val imsNetworkNc = NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_IMS) + .addCapability(NET_CAPABILITY_NOT_SUSPENDED) + .build() + val imsNetworkNr = NetworkRequest.Builder() + .clearCapabilities() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_IMS) + .build() + val imsNetworkLp = LinkProperties().apply { + interfaceName = IMS_CELL_IFNAME + } + val imsNetworkCb = TestableNetworkCallback() + cm.requestNetwork(imsNetworkNr, imsNetworkCb) + val imsNetworkAgent = Agent(nc = imsNetworkNc, lp = imsNetworkLp) + val imsNetworkNetId = imsNetworkAgent.network.netId.toString() + + dataNetworkAgent.connect() + dataNetworkCb.expectAvailableCallbacks(dataNetworkAgent.network, validated = false) + + imsNetworkAgent.connect() + imsNetworkCb.expectAvailableCallbacks(imsNetworkAgent.network, validated = false) + + // Both cell networks have idleTimers + verify(netd).idletimerAddInterface(eq(DATA_CELL_IFNAME), anyInt(), eq(dataNetworkNetId)) + verify(netd).idletimerAddInterface(eq(IMS_CELL_IFNAME), anyInt(), eq(imsNetworkNetId)) + verify(netd, never()).idletimerRemoveInterface(eq(DATA_CELL_IFNAME), anyInt(), + eq(dataNetworkNetId)) + verify(netd, never()).idletimerRemoveInterface(eq(IMS_CELL_IFNAME), anyInt(), + eq(imsNetworkNetId)) + + dataNetworkAgent.disconnect() + dataNetworkCb.expect(dataNetworkAgent.network) + verify(netd).idletimerRemoveInterface(eq(DATA_CELL_IFNAME), anyInt(), eq(dataNetworkNetId)) + + imsNetworkAgent.disconnect() + imsNetworkCb.expect(imsNetworkAgent.network) + verify(netd).idletimerRemoveInterface(eq(IMS_CELL_IFNAME), anyInt(), eq(imsNetworkNetId)) + + cm.unregisterNetworkCallback(dataNetworkCb) + cm.unregisterNetworkCallback(imsNetworkCb) + } +} + +internal fun CSContext.expectDataActivityBroadcast( + deviceType: Int, + isActive: Boolean, + tsNanos: Long +) { + assertNotNull(orderedBroadcastAsUserHistory.poll(BROADCAST_TIMEOUT_MS) { + intent -> intent.action.equals(ACTION_DATA_ACTIVITY_CHANGE) && + intent.getIntExtra(EXTRA_DEVICE_TYPE, -1) == deviceType && + intent.getBooleanExtra(EXTRA_IS_ACTIVE, !isActive) == isActive && + intent.getLongExtra(EXTRA_REALTIME_NS, -1) == tsNanos + }) +} diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt index db70599abe..a28fef7ebc 100644 --- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt +++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt @@ -43,6 +43,7 @@ import android.net.NetworkScore import android.net.PacProxyManager import android.net.networkstack.NetworkStackClientBase import android.os.BatteryStatsManager +import android.os.Bundle import android.os.Handler import android.os.HandlerThread import android.os.UserHandle @@ -54,6 +55,7 @@ import androidx.test.platform.app.InstrumentationRegistry import com.android.internal.app.IBatteryStats import com.android.internal.util.test.BroadcastInterceptingContext import com.android.modules.utils.build.SdkLevel +import com.android.net.module.util.ArrayTrackRecord import com.android.networkstack.apishim.common.UnsupportedApiLevelException import com.android.server.connectivity.AutomaticOnOffKeepaliveTracker import com.android.server.connectivity.CarrierPrivilegeAuthenticator @@ -65,6 +67,7 @@ import com.android.server.connectivity.ProxyTracker import com.android.testutils.visibleOnHandlerThread import com.android.testutils.waitForIdle import java.util.concurrent.Executors +import kotlin.test.assertNull import kotlin.test.fail import org.mockito.AdditionalAnswers.delegatesTo import org.mockito.Mockito.doAnswer @@ -72,6 +75,7 @@ import org.mockito.Mockito.doReturn import org.mockito.Mockito.mock internal const val HANDLER_TIMEOUT_MS = 2_000 +internal const val BROADCAST_TIMEOUT_MS = 3_000L internal const val TEST_PACKAGE_NAME = "com.android.test.package" internal const val WIFI_WOL_IFNAME = "test_wlan_wol" internal val LOCAL_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1") @@ -282,6 +286,26 @@ open class CSTest { Context.STATS_MANAGER -> null // Stats manager is final and can't be mocked else -> super.getSystemService(serviceName) } + + internal val orderedBroadcastAsUserHistory = ArrayTrackRecord().newReadHead() + + fun expectNoDataActivityBroadcast(timeoutMs: Int) { + assertNull(orderedBroadcastAsUserHistory.poll( + timeoutMs.toLong()) { intent -> true }) + } + + override fun sendOrderedBroadcastAsUser( + intent: Intent, + user: UserHandle, + receiverPermission: String?, + resultReceiver: BroadcastReceiver?, + scheduler: Handler?, + initialCode: Int, + initialData: String?, + initialExtras: Bundle? + ) { + orderedBroadcastAsUserHistory.add(intent) + } } // Utility methods for subclasses to use