Merge changes from topic "multi_network_activity_tracking" into main

* changes:
  Make ConnectivityService update BatteryStats radio power state
  Track multiple network activities on V+
  Use netId as idleTimer label on V+
This commit is contained in:
Motomu Utsumi
2023-11-15 13:58:37 +00:00
committed by Gerrit Code Review
4 changed files with 644 additions and 93 deletions

View File

@@ -155,6 +155,8 @@ import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALID
import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_SUCCESS;
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.telephony.DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
import static android.telephony.DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
import static com.android.server.ConnectivityService.DELAY_DESTROY_FROZEN_SOCKETS_VERSION;
import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
@@ -640,8 +642,8 @@ public class ConnectivityServiceTest {
// BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the
// underlying binder calls.
final BatteryStatsManager mBatteryStatsManager =
new BatteryStatsManager(mock(IBatteryStats.class));
final IBatteryStats mIBatteryStats = mock(IBatteryStats.class);
final BatteryStatsManager mBatteryStatsManager = new BatteryStatsManager(mIBatteryStats);
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -10807,6 +10809,11 @@ public class ConnectivityServiceTest {
expectNativeNetworkCreated(netId, permission, iface, null /* inOrder */);
}
private int getIdleTimerLabel(int netId, int transportType) {
return ConnectivityService.LegacyNetworkActivityTracker.getIdleTimerLabel(
mDeps.isAtLeastV(), netId, transportType);
}
@Test
public void testStackedLinkProperties() throws Exception {
final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24");
@@ -11048,7 +11055,7 @@ public class ConnectivityServiceTest {
networkCallback.expect(LOST, mCellAgent);
networkCallback.assertNoCallback();
verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
eq(Integer.toString(TRANSPORT_CELLULAR)));
eq(Integer.toString(getIdleTimerLabel(cellNetId, TRANSPORT_CELLULAR))));
verify(mMockNetd).networkDestroy(cellNetId);
if (mDeps.isAtLeastU()) {
verify(mMockNetd).setNetworkAllowlist(any());
@@ -11107,7 +11114,7 @@ public class ConnectivityServiceTest {
}
verify(mMockNetd).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
eq(Integer.toString(TRANSPORT_CELLULAR)));
eq(Integer.toString(getIdleTimerLabel(cellNetId, TRANSPORT_CELLULAR))));
verify(mMockNetd).networkDestroy(cellNetId);
if (mDeps.isAtLeastU()) {
verify(mMockNetd).setNetworkAllowlist(any());
@@ -11362,8 +11369,21 @@ public class ConnectivityServiceTest {
final ConditionVariable onNetworkActiveCv = new ConditionVariable();
final ConnectivityManager.OnNetworkActiveListener listener = onNetworkActiveCv::open;
TestNetworkCallback defaultCallback = new TestNetworkCallback();
testAndCleanup(() -> {
mCm.registerDefaultNetworkCallback(defaultCallback);
agent.connect(true);
defaultCallback.expectAvailableThenValidatedCallbacks(agent);
if (transportType == TRANSPORT_CELLULAR) {
verify(mIBatteryStats).noteMobileRadioPowerState(eq(DC_POWER_STATE_HIGH),
anyLong() /* timestampNs */, eq(NETWORK_ACTIVITY_NO_UID));
} else if (transportType == TRANSPORT_WIFI) {
verify(mIBatteryStats).noteWifiRadioPowerState(eq(DC_POWER_STATE_HIGH),
anyLong() /* timestampNs */, eq(NETWORK_ACTIVITY_NO_UID));
}
clearInvocations(mIBatteryStats);
final int idleTimerLabel = getIdleTimerLabel(agent.getNetwork().netId, transportType);
// Network is considered active when the network becomes the default network.
assertTrue(mCm.isDefaultNetworkActive());
@@ -11372,19 +11392,57 @@ public class ConnectivityServiceTest {
// Interface goes to inactive state
netdUnsolicitedEventListener.onInterfaceClassActivityChanged(false /* isActive */,
transportType, TIMESTAMP, NETWORK_ACTIVITY_NO_UID);
idleTimerLabel, TIMESTAMP, NETWORK_ACTIVITY_NO_UID);
mServiceContext.expectDataActivityBroadcast(legacyType, false /* isActive */,
TIMESTAMP);
assertFalse(onNetworkActiveCv.block(TEST_CALLBACK_TIMEOUT_MS));
assertFalse(mCm.isDefaultNetworkActive());
if (mDeps.isAtLeastV()) {
if (transportType == TRANSPORT_CELLULAR) {
verify(mIBatteryStats).noteMobileRadioPowerState(eq(DC_POWER_STATE_LOW),
anyLong() /* timestampNs */, eq(NETWORK_ACTIVITY_NO_UID));
} else if (transportType == TRANSPORT_WIFI) {
verify(mIBatteryStats).noteWifiRadioPowerState(eq(DC_POWER_STATE_LOW),
anyLong() /* timestampNs */, eq(NETWORK_ACTIVITY_NO_UID));
}
} else {
// If TrackMultiNetworks is disabled, LegacyNetworkActivityTracker does not call
// BatteryStats API by the netd activity change callback since BatteryStatsService
// listen to netd callback via NetworkManagementService and update battery stats by
// itself.
verify(mIBatteryStats, never())
.noteMobileRadioPowerState(anyInt(), anyLong(), anyInt());
verify(mIBatteryStats, never())
.noteWifiRadioPowerState(anyInt(), anyLong(), anyInt());
}
// Interface goes to active state
netdUnsolicitedEventListener.onInterfaceClassActivityChanged(true /* isActive */,
transportType, TIMESTAMP, TEST_PACKAGE_UID);
idleTimerLabel, TIMESTAMP, TEST_PACKAGE_UID);
mServiceContext.expectDataActivityBroadcast(legacyType, true /* isActive */, TIMESTAMP);
assertTrue(onNetworkActiveCv.block(TEST_CALLBACK_TIMEOUT_MS));
assertTrue(mCm.isDefaultNetworkActive());
if (mDeps.isAtLeastV()) {
if (transportType == TRANSPORT_CELLULAR) {
verify(mIBatteryStats).noteMobileRadioPowerState(eq(DC_POWER_STATE_HIGH),
anyLong() /* timestampNs */, eq(TEST_PACKAGE_UID));
} else if (transportType == TRANSPORT_WIFI) {
verify(mIBatteryStats).noteWifiRadioPowerState(eq(DC_POWER_STATE_HIGH),
anyLong() /* timestampNs */, eq(TEST_PACKAGE_UID));
}
} else {
// If TrackMultiNetworks is disabled, LegacyNetworkActivityTracker does not call
// BatteryStats API by the netd activity change callback since BatteryStatsService
// listen to netd callback via NetworkManagementService and update battery stats by
// itself.
verify(mIBatteryStats, never())
.noteMobileRadioPowerState(anyInt(), anyLong(), anyInt());
verify(mIBatteryStats, never())
.noteWifiRadioPowerState(anyInt(), anyLong(), anyInt());
}
}, () -> { // Cleanup
mCm.unregisterNetworkCallback(defaultCallback);
}, () -> { // Cleanup
mCm.removeDefaultNetworkActiveListener(listener);
}, () -> { // Cleanup
agent.disconnect();
@@ -11432,12 +11490,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
@@ -11467,15 +11526,19 @@ public class ConnectivityServiceTest {
mCm.registerNetworkCallback(networkRequest, networkCallback);
mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
final String cellIdleTimerLabel = Integer.toString(getIdleTimerLabel(
mCellAgent.getNetwork().netId, TRANSPORT_CELLULAR));
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
mCellAgent.sendLinkProperties(cellLp);
mCellAgent.connect(true);
networkCallback.expectAvailableThenValidatedCallbacks(mCellAgent);
verify(mMockNetd, times(1)).idletimerAddInterface(eq(MOBILE_IFNAME), anyInt(),
eq(Integer.toString(TRANSPORT_CELLULAR)));
eq(cellIdleTimerLabel));
mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
String wifiIdleTimerLabel = Integer.toString(getIdleTimerLabel(
mWiFiAgent.getNetwork().netId, TRANSPORT_WIFI));
final LinkProperties wifiLp = new LinkProperties();
wifiLp.setInterfaceName(WIFI_IFNAME);
mWiFiAgent.sendLinkProperties(wifiLp);
@@ -11486,9 +11549,18 @@ public class ConnectivityServiceTest {
networkCallback.expectLosing(mCellAgent);
networkCallback.expectCaps(mWiFiAgent, c -> c.hasCapability(NET_CAPABILITY_VALIDATED));
verify(mMockNetd, times(1)).idletimerAddInterface(eq(WIFI_IFNAME), anyInt(),
eq(Integer.toString(TRANSPORT_WIFI)));
verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
eq(Integer.toString(TRANSPORT_CELLULAR)));
eq(wifiIdleTimerLabel));
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);
@@ -11496,13 +11568,20 @@ public class ConnectivityServiceTest {
networkCallback.expect(LOST, mWiFiAgent);
assertNoCallbacks(networkCallback);
verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(WIFI_IFNAME), anyInt(),
eq(Integer.toString(TRANSPORT_WIFI)));
verify(mMockNetd, times(1)).idletimerAddInterface(eq(MOBILE_IFNAME), anyInt(),
eq(Integer.toString(TRANSPORT_CELLULAR)));
eq(wifiIdleTimerLabel));
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);
mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
wifiIdleTimerLabel = Integer.toString(getIdleTimerLabel(
mWiFiAgent.getNetwork().netId, TRANSPORT_WIFI));
wifiLp.setInterfaceName(WIFI_IFNAME);
mWiFiAgent.sendLinkProperties(wifiLp);
mWiFiAgent.connect(true);
@@ -11510,20 +11589,30 @@ public class ConnectivityServiceTest {
networkCallback.expectLosing(mCellAgent);
networkCallback.expectCaps(mWiFiAgent, c -> c.hasCapability(NET_CAPABILITY_VALIDATED));
verify(mMockNetd, times(1)).idletimerAddInterface(eq(WIFI_IFNAME), anyInt(),
eq(Integer.toString(TRANSPORT_WIFI)));
verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
eq(Integer.toString(TRANSPORT_CELLULAR)));
eq(wifiIdleTimerLabel));
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(Integer.toString(TRANSPORT_CELLULAR)));
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));
@@ -11532,12 +11621,27 @@ public class ConnectivityServiceTest {
mWiFiAgent.disconnect();
b.expectBroadcast();
verify(mMockNetd, times(1)).idletimerRemoveInterface(eq(WIFI_IFNAME), anyInt(),
eq(Integer.toString(TRANSPORT_WIFI)));
eq(wifiIdleTimerLabel));
// Clean up
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]);
@@ -18728,6 +18832,7 @@ public class ConnectivityServiceTest {
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(transportToTestIfaceName(transportType));
final TestNetworkAgentWrapper agent = new TestNetworkAgentWrapper(transportType, lp);
final int idleTimerLabel = getIdleTimerLabel(agent.getNetwork().netId, transportType);
testAndCleanup(() -> {
final UidFrozenStateChangedCallback uidFrozenStateChangedCallback =
getUidFrozenStateChangedCallback().get();
@@ -18740,7 +18845,7 @@ public class ConnectivityServiceTest {
if (freezeWithNetworkInactive) {
// Make network inactive
netdUnsolicitedEventListener.onInterfaceClassActivityChanged(false /* isActive */,
transportType, TIMESTAMP, NETWORK_ACTIVITY_NO_UID);
idleTimerLabel, TIMESTAMP, NETWORK_ACTIVITY_NO_UID);
}
// Freeze TEST_FROZEN_UID and TEST_UNFROZEN_UID
@@ -18764,7 +18869,7 @@ public class ConnectivityServiceTest {
// Make network active
netdUnsolicitedEventListener.onInterfaceClassActivityChanged(true /* isActive */,
transportType, TIMESTAMP, TEST_PACKAGE_UID);
idleTimerLabel, TIMESTAMP, TEST_PACKAGE_UID);
waitForIdle();
if (expectDelay) {
@@ -18783,8 +18888,8 @@ public class ConnectivityServiceTest {
@Test
@IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
public void testDelayFrozenUidSocketDestroy_ActiveCellular() throws Exception {
doTestDelayFrozenUidSocketDestroy(TRANSPORT_CELLULAR,
false /* freezeWithNetworkInactive */, false /* expectDelay */);
doTestDelayFrozenUidSocketDestroy(TRANSPORT_CELLULAR, false /* freezeWithNetworkInactive */,
false /* expectDelay */);
}
@Test
@@ -18792,22 +18897,22 @@ public class ConnectivityServiceTest {
public void testDelayFrozenUidSocketDestroy_InactiveCellular() throws Exception {
// When the default network is cellular and cellular network is inactive, closing socket
// is delayed.
doTestDelayFrozenUidSocketDestroy(TRANSPORT_CELLULAR,
true /* freezeWithNetworkInactive */, true /* expectDelay */);
doTestDelayFrozenUidSocketDestroy(TRANSPORT_CELLULAR, true /* freezeWithNetworkInactive */,
true /* expectDelay */);
}
@Test
@IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
public void testDelayFrozenUidSocketDestroy_ActiveWifi() throws Exception {
doTestDelayFrozenUidSocketDestroy(TRANSPORT_WIFI,
false /* freezeWithNetworkInactive */, false /* expectDelay */);
doTestDelayFrozenUidSocketDestroy(TRANSPORT_WIFI, false /* freezeWithNetworkInactive */,
false /* expectDelay */);
}
@Test
@IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
public void testDelayFrozenUidSocketDestroy_InactiveWifi() throws Exception {
doTestDelayFrozenUidSocketDestroy(TRANSPORT_WIFI,
true /* freezeWithNetworkInactive */, false /* expectDelay */);
doTestDelayFrozenUidSocketDestroy(TRANSPORT_WIFI, true /* freezeWithNetworkInactive */,
false /* expectDelay */);
}
/**
@@ -18828,6 +18933,8 @@ public class ConnectivityServiceTest {
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
final int idleTimerLabel =
getIdleTimerLabel(mCellAgent.getNetwork().netId, TRANSPORT_CELLULAR);
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
@@ -18837,7 +18944,7 @@ public class ConnectivityServiceTest {
// Make cell network inactive
netdUnsolicitedEventListener.onInterfaceClassActivityChanged(false /* isActive */,
TRANSPORT_CELLULAR, TIMESTAMP, NETWORK_ACTIVITY_NO_UID);
idleTimerLabel, TIMESTAMP, NETWORK_ACTIVITY_NO_UID);
// Freeze TEST_FROZEN_UID
final int[] uids = {TEST_FROZEN_UID};

View File

@@ -0,0 +1,267 @@
/*
* 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 android.telephony.DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
import android.telephony.DataConnectionRealTimeInfo.DC_POWER_STATE_LOW
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.anyLong
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.never
import org.mockito.Mockito.timeout
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 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 batteryStatsInorder = inOrder(batteryStats)
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)
batteryStatsInorder.verify(batteryStats).noteWifiRadioPowerState(eq(DC_POWER_STATE_HIGH),
anyLong() /* timestampNs */, eq(NETWORK_ACTIVITY_NO_UID))
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
// But cellular radio power state is updated
assertFalse(onNetworkActiveCv.block(TIMEOUT_MS))
context.expectNoDataActivityBroadcast(0 /* timeoutMs */)
assertTrue(cm.isDefaultNetworkActive)
batteryStatsInorder.verify(batteryStats).noteMobileRadioPowerState(eq(DC_POWER_STATE_LOW),
anyLong() /* timestampNs */, eq(NETWORK_ACTIVITY_NO_UID))
// 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
// But cellular radio power state is updated
assertFalse(onNetworkActiveCv.block(TIMEOUT_MS))
context.expectNoDataActivityBroadcast(0 /* timeoutMs */)
assertTrue(cm.isDefaultNetworkActive)
batteryStatsInorder.verify(batteryStats).noteMobileRadioPowerState(eq(DC_POWER_STATE_HIGH),
anyLong() /* timestampNs */, eq(PACKAGE_UID))
cm.unregisterNetworkCallback(cellCb)
cm.unregisterNetworkCallback(defaultCb)
cm.removeDefaultNetworkActiveListener(listener)
}
@Test
fun testDataActivityTracking_MultiCellNetwork() {
val netdUnsolicitedEventListener = getRegisteredNetdUnsolicitedEventListener()
val batteryStatsInorder = inOrder(batteryStats)
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))
// Both cell networks go to inactive state
netdUnsolicitedEventListener.onInterfaceClassActivityChanged(false /* isActive */,
imsNetworkAgent.network.netId, TIMESTAMP, NETWORK_ACTIVITY_NO_UID)
netdUnsolicitedEventListener.onInterfaceClassActivityChanged(false /* isActive */,
dataNetworkAgent.network.netId, TIMESTAMP, NETWORK_ACTIVITY_NO_UID)
// Data cell network goes to active state. This should update the cellular radio power state
netdUnsolicitedEventListener.onInterfaceClassActivityChanged(true /* isActive */,
dataNetworkAgent.network.netId, TIMESTAMP, PACKAGE_UID)
batteryStatsInorder.verify(batteryStats, timeout(TIMEOUT_MS)).noteMobileRadioPowerState(
eq(DC_POWER_STATE_HIGH), anyLong() /* timestampNs */, eq(PACKAGE_UID))
// Ims cell network goes to active state. But this should not update the cellular radio
// power state since cellular radio power state is already high
netdUnsolicitedEventListener.onInterfaceClassActivityChanged(true /* isActive */,
imsNetworkAgent.network.netId, TIMESTAMP, PACKAGE_UID)
waitForIdle()
batteryStatsInorder.verify(batteryStats, never()).noteMobileRadioPowerState(anyInt(),
anyLong() /* timestampNs */, anyInt())
// Data cell network goes to inactive state. But this should not update the cellular radio
// power state ims cell network is still active state
netdUnsolicitedEventListener.onInterfaceClassActivityChanged(false /* isActive */,
dataNetworkAgent.network.netId, TIMESTAMP, NETWORK_ACTIVITY_NO_UID)
waitForIdle()
batteryStatsInorder.verify(batteryStats, never()).noteMobileRadioPowerState(anyInt(),
anyLong() /* timestampNs */, anyInt())
// Ims cell network goes to inactive state.
// This should update the cellular radio power state
netdUnsolicitedEventListener.onInterfaceClassActivityChanged(false /* isActive */,
imsNetworkAgent.network.netId, TIMESTAMP, NETWORK_ACTIVITY_NO_UID)
batteryStatsInorder.verify(batteryStats, timeout(TIMEOUT_MS)).noteMobileRadioPowerState(
eq(DC_POWER_STATE_LOW), anyLong() /* timestampNs */, eq(NETWORK_ACTIVITY_NO_UID))
dataNetworkAgent.disconnect()
dataNetworkCb.expect<Lost>(dataNetworkAgent.network)
verify(netd).idletimerRemoveInterface(eq(DATA_CELL_IFNAME), anyInt(), eq(dataNetworkNetId))
imsNetworkAgent.disconnect()
imsNetworkCb.expect<Lost>(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
})
}

View File

@@ -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
@@ -64,14 +66,16 @@ import com.android.server.connectivity.MultinetworkPolicyTrackerTestDependencies
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
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.mock
import java.util.concurrent.Executors
import kotlin.test.fail
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")
@@ -155,7 +159,8 @@ open class CSTest {
val proxyTracker = ProxyTracker(context, mock<Handler>(), 16 /* EVENT_PROXY_HAS_CHANGED */)
val alarmManager = makeMockAlarmManager()
val systemConfigManager = makeMockSystemConfigManager()
val batteryManager = BatteryStatsManager(mock<IBatteryStats>())
val batteryStats = mock<IBatteryStats>()
val batteryManager = BatteryStatsManager(batteryStats)
val telephonyManager = mock<TelephonyManager>().also {
doReturn(true).`when`(it).isDataCapable()
}
@@ -285,6 +290,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<Intent>().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