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

@@ -1770,7 +1770,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
mUserAllContext.registerReceiver(mPackageIntentReceiver, packageIntentFilter,
null /* broadcastPermission */, mHandler);
mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mNetd, mHandler);
// TrackMultiNetworkActivities feature should be enabled by trunk stable flag.
// But reading the trunk stable flags from mainline modules is not supported yet.
// So enabling this feature on V+ release.
mTrackMultiNetworkActivities = mDeps.isAtLeastV();
mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mNetd, mHandler,
mTrackMultiNetworkActivities);
final NetdCallback netdCallback = new NetdCallback();
try {
@@ -3246,9 +3251,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void handleReportNetworkActivity(final NetworkActivityParams params) {
mNetworkActivityTracker.handleReportNetworkActivity(params);
final boolean isCellNetworkActivity;
if (mTrackMultiNetworkActivities) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(params.label);
// nai could be null if netd receives a netlink message and calls the network
// activity change callback after the network is unregistered from ConnectivityService.
isCellNetworkActivity = nai != null
&& nai.networkCapabilities.hasTransport(TRANSPORT_CELLULAR);
} else {
isCellNetworkActivity = params.label == TRANSPORT_CELLULAR;
}
if (mDelayDestroyFrozenSockets
&& params.isActive
&& params.label == TRANSPORT_CELLULAR
&& isCellNetworkActivity
&& !mPendingFrozenUids.isEmpty()) {
closePendingFrozenSockets();
}
@@ -4965,6 +4981,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.
@@ -5018,12 +5039,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());
}
@@ -9644,7 +9660,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);
@@ -10544,6 +10560,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();
@@ -11735,8 +11760,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
*/
private static final class NetworkActivityParams {
public final boolean isActive;
// Label used for idle timer. Transport type is used as label.
// label is int since NMS was using the identifier as int, and it has not been changed
// If TrackMultiNetworkActivities is enabled, idleTimer label is netid.
// If TrackMultiNetworkActivities is disabled, idleTimer label is transport type.
public final int label;
public final long timestampNs;
// Uid represents the uid that was responsible for waking the radio.
@@ -11778,13 +11803,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
private final boolean mTrackMultiNetworkActivities;
private final LegacyNetworkActivityTracker mNetworkActivityTracker;
/**
* Class used for updating network activity tracking with netd and notify network activity
* changes.
*/
private static final class LegacyNetworkActivityTracker {
@VisibleForTesting
public static final class LegacyNetworkActivityTracker {
private static final int NO_UID = -1;
private final Context mContext;
private final INetd mNetd;
@@ -11796,8 +11823,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
// If there is no default network, default network is considered active to keep the existing
// behavior. Initial value is used until first connect to the default network.
private volatile boolean mIsDefaultNetworkActive = true;
private Network mDefaultNetwork;
// Key is netId. Value is configured idle timer information.
private final SparseArray<IdleTimerParams> mActiveIdleTimers = new SparseArray<>();
private final boolean mTrackMultiNetworkActivities;
// Store netIds of Wi-Fi networks whose idletimers report that they are active
private final Set<Integer> mActiveWifiNetworks = new ArraySet<>();
// Store netIds of cellular networks whose idletimers report that they are active
private final Set<Integer> mActiveCellularNetworks = new ArraySet<>();
private static class IdleTimerParams {
public final int timeout;
@@ -11810,10 +11843,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
LegacyNetworkActivityTracker(@NonNull Context context, @NonNull INetd netd,
@NonNull Handler handler) {
@NonNull Handler handler, boolean trackMultiNetworkActivities) {
mContext = context;
mNetd = netd;
mHandler = handler;
mTrackMultiNetworkActivities = trackMultiNetworkActivities;
}
private void ensureRunningOnConnectivityServiceThread() {
@@ -11823,19 +11857,97 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
public void handleReportNetworkActivity(NetworkActivityParams activityParams) {
ensureRunningOnConnectivityServiceThread();
/**
* Update network activity and call BatteryStats to update radio power state if the
* mobile or Wi-Fi activity is changed.
* LegacyNetworkActivityTracker considers the mobile network is active if at least one
* mobile network is active since BatteryStatsService only maintains a single power state
* for the mobile network.
* The Wi-Fi network is also the same.
*
* {@link #setupDataActivityTracking} and {@link #removeDataActivityTracking} use
* TRANSPORT_CELLULAR as the transportType argument if the network has both cell and Wi-Fi
* transports.
*/
private void maybeUpdateRadioPowerState(final int netId, final int transportType,
final boolean isActive, final int uid) {
if (transportType != TRANSPORT_WIFI && transportType != TRANSPORT_CELLULAR) {
Log.e(TAG, "Unexpected transportType in maybeUpdateRadioPowerState: "
+ transportType);
return;
}
final Set<Integer> activeNetworks = transportType == TRANSPORT_WIFI
? mActiveWifiNetworks : mActiveCellularNetworks;
final boolean wasEmpty = activeNetworks.isEmpty();
if (isActive) {
activeNetworks.add(netId);
} else {
activeNetworks.remove(netId);
}
if (wasEmpty != activeNetworks.isEmpty()) {
updateRadioPowerState(isActive, transportType, uid);
}
}
private void handleDefaultNetworkActivity(final int transportType,
final boolean isActive, final long timestampNs) {
mIsDefaultNetworkActive = isActive;
sendDataActivityBroadcast(transportTypeToLegacyType(transportType),
isActive, timestampNs);
if (isActive) {
reportNetworkActive();
}
}
private void handleReportNetworkActivityWithNetIdLabel(
NetworkActivityParams activityParams) {
final int netId = activityParams.label;
final IdleTimerParams idleTimerParams = mActiveIdleTimers.get(netId);
if (idleTimerParams == null) {
// This network activity change is not tracked anymore
// This can happen if netd callback post activity change event message but idle
// timer is removed before processing this message.
return;
}
// TODO: if a network changes transports, storing the transport type in the
// IdleTimerParams is not correct. Consider getting it from the network's
// NetworkCapabilities instead.
final int transportType = idleTimerParams.transportType;
maybeUpdateRadioPowerState(netId, transportType,
activityParams.isActive, activityParams.uid);
if (mDefaultNetwork == null || mDefaultNetwork.netId != netId) {
// This activity change is not for the default network.
return;
}
handleDefaultNetworkActivity(transportType, activityParams.isActive,
activityParams.timestampNs);
}
private void handleReportNetworkActivityWithTransportTypeLabel(
NetworkActivityParams activityParams) {
if (mActiveIdleTimers.size() == 0) {
// This activity change is not for the current default network.
// This can happen if netd callback post activity change event message but
// the default network is lost before processing this message.
return;
}
sendDataActivityBroadcast(transportTypeToLegacyType(activityParams.label),
activityParams.isActive, activityParams.timestampNs);
mIsDefaultNetworkActive = activityParams.isActive;
if (mIsDefaultNetworkActive) {
reportNetworkActive();
handleDefaultNetworkActivity(activityParams.label, activityParams.isActive,
activityParams.timestampNs);
}
/**
* Handle network activity change
*/
public void handleReportNetworkActivity(NetworkActivityParams activityParams) {
ensureRunningOnConnectivityServiceThread();
if (mTrackMultiNetworkActivities) {
handleReportNetworkActivityWithNetIdLabel(activityParams);
} else {
handleReportNetworkActivityWithTransportTypeLabel(activityParams);
}
}
@@ -11891,6 +12003,30 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
/**
* Get idle timer label
*/
@VisibleForTesting
public static int getIdleTimerLabel(final boolean trackMultiNetworkActivities,
final int netId, final int transportType) {
return trackMultiNetworkActivities ? netId : transportType;
}
private boolean maybeCreateIdleTimer(
String iface, int netId, int timeout, int transportType) {
if (timeout <= 0 || iface == null) return false;
try {
final String label = Integer.toString(getIdleTimerLabel(
mTrackMultiNetworkActivities, netId, transportType));
mNetd.idletimerAddInterface(iface, timeout, label);
mActiveIdleTimers.put(netId, new IdleTimerParams(timeout, transportType));
return true;
} catch (Exception e) {
loge("Exception in createIdleTimer", e);
return false;
}
}
/**
* Setup data activity tracking for the given network.
*
@@ -11900,13 +12036,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,
@@ -11922,25 +12062,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
return false; // do not track any other networks
}
updateRadioPowerState(true /* isActive */, type);
if (timeout > 0 && iface != null) {
try {
mActiveIdleTimers.put(netId, new IdleTimerParams(timeout, type));
mNetd.idletimerAddInterface(iface, timeout, Integer.toString(type));
return true;
} catch (Exception e) {
// You shall not crash!
loge("Exception in setupDataActivityTracking " + e);
final boolean hasIdleTimer = maybeCreateIdleTimer(iface, netId, timeout, type);
if (hasIdleTimer || !mTrackMultiNetworkActivities) {
// If trackMultiNetwork is disabled, NetworkActivityTracker updates radio power
// state in all cases. If trackMultiNetwork is enabled, it updates radio power
// state only about a network that has an idletimer.
maybeUpdateRadioPowerState(netId, type, true /* isActive */, NO_UID);
}
}
return false;
return hasIdleTimer;
}
/**
* 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;
@@ -11948,7 +12084,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;
@@ -11957,16 +12096,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
try {
updateRadioPowerState(false /* isActive */, type);
maybeUpdateRadioPowerState(netId, type, false /* isActive */, NO_UID);
final IdleTimerParams params = mActiveIdleTimers.get(netId);
if (params == null) {
// IdleTimer is not added if the configured timeout is 0 or negative value
return;
}
mActiveIdleTimers.remove(netId);
final String label = Integer.toString(getIdleTimerLabel(
mTrackMultiNetworkActivities, netId, params.transportType));
// The call fails silently if no idle timer setup for this interface
mNetd.idletimerRemoveInterface(iface, params.timeout,
Integer.toString(params.transportType));
mNetd.idletimerRemoveInterface(iface, params.timeout, label);
} catch (Exception e) {
// You shall not crash!
loge("Exception in removeDataActivityTracking " + e);
@@ -11976,12 +12116,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void updateDefaultNetworkActivity(NetworkAgentInfo defaultNetwork,
boolean hasIdleTimer) {
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 {
mDefaultNetwork = null;
// If there is no default network, default network is considered active to keep the
// existing behavior.
mIsDefaultNetworkActive = true;
@@ -11989,29 +12132,34 @@ 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);
}
}
private void updateRadioPowerState(boolean isActive, int transportType) {
private void updateRadioPowerState(boolean isActive, int transportType, int uid) {
final BatteryStatsManager bs = mContext.getSystemService(BatteryStatsManager.class);
switch (transportType) {
case NetworkCapabilities.TRANSPORT_CELLULAR:
bs.reportMobileRadioPowerState(isActive, NO_UID);
bs.reportMobileRadioPowerState(isActive, uid);
break;
case NetworkCapabilities.TRANSPORT_WIFI:
bs.reportWifiRadioPowerState(isActive, NO_UID);
bs.reportWifiRadioPowerState(isActive, uid);
break;
default:
logw("Untracked transport type:" + transportType);
@@ -12031,7 +12179,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
public void dump(IndentingPrintWriter pw) {
pw.print("mTrackMultiNetworkActivities="); pw.println(mTrackMultiNetworkActivities);
pw.print("mIsDefaultNetworkActive="); pw.println(mIsDefaultNetworkActive);
pw.print("mDefaultNetwork="); pw.println(mDefaultNetwork);
pw.println("Idle timers:");
try {
for (int i = 0; i < mActiveIdleTimers.size(); i++) {
@@ -12040,11 +12190,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
pw.print(" timeout="); pw.print(params.timeout);
pw.print(" type="); pw.println(params.transportType);
}
pw.println("WiFi active networks: " + mActiveWifiNetworks);
pw.println("Cellular active networks: " + mActiveCellularNetworks);
} catch (Exception e) {
// mActiveIdleTimers should only be accessed from handler thread, except dump().
// As dump() is never called in normal usage, it would be needlessly expensive
// to lock the collection only for its benefit.
// Also, mActiveIdleTimers is not expected to be updated frequently.
// mActiveIdleTimers, mActiveWifiNetworks, and mActiveCellularNetworks should only
// be accessed from handler thread, except dump(). As dump() is never called in
// normal usage, it would be needlessly expensive to lock the collection only for
// its benefit. Also, they are not expected to be updated frequently.
// So catching the exception and logging.
pw.println("Failed to dump NetworkActivityTracker: " + e);
}

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,18 +11392,56 @@ 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
@@ -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)));
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)));
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)));
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);
waitForIdle();
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.
waitForIdle();
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