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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user