Add LocalNetworkInfo and send callbacks when it changes

Test: CSLocalAgentTest
Change-Id: I8caca97b891081f9212a01d428a34ed1a08d5126
This commit is contained in:
Chalard Jean
2023-10-07 19:21:45 +09:00
parent 9f76411b63
commit 22350c93b4
7 changed files with 355 additions and 56 deletions

View File

@@ -0,0 +1,20 @@
/**
*
* 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 android.net;
parcelable LocalNetworkInfo;

View File

@@ -23,6 +23,7 @@ android\.net\.IConnectivityDiagnosticsCallback(\$.+)?
# of these classes must be protected by a check for >= S SDK. # of these classes must be protected by a check for >= S SDK.
# It's unlikely anybody else declares a hidden class with this name ? # It's unlikely anybody else declares a hidden class with this name ?
android\.net\.RoutingCoordinatorManager(\$.+)? android\.net\.RoutingCoordinatorManager(\$.+)?
android\.net\.LocalNetworkInfo(\$.+)?
# KeepaliveUtils is used by ConnectivityManager CTS # KeepaliveUtils is used by ConnectivityManager CTS
# TODO: move into service-connectivity so framework-connectivity stops using # TODO: move into service-connectivity so framework-connectivity stops using

View File

@@ -3963,16 +3963,21 @@ public class ConnectivityManager {
* @param network The {@link Network} of the satisfying network. * @param network The {@link Network} of the satisfying network.
* @param networkCapabilities The {@link NetworkCapabilities} of the satisfying network. * @param networkCapabilities The {@link NetworkCapabilities} of the satisfying network.
* @param linkProperties The {@link LinkProperties} of the satisfying network. * @param linkProperties The {@link LinkProperties} of the satisfying network.
* @param localInfo The {@link LocalNetworkInfo} of the satisfying network, or null
* if this network is not a local network.
* @param blocked Whether access to the {@link Network} is blocked due to system policy. * @param blocked Whether access to the {@link Network} is blocked due to system policy.
* @hide * @hide
*/ */
public final void onAvailable(@NonNull Network network, public final void onAvailable(@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities, @NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties, @BlockedReason int blocked) { @NonNull LinkProperties linkProperties,
@Nullable LocalNetworkInfo localInfo,
@BlockedReason int blocked) {
// Internally only this method is called when a new network is available, and // Internally only this method is called when a new network is available, and
// it calls the callback in the same way and order that older versions used // it calls the callback in the same way and order that older versions used
// to call so as not to change the behavior. // to call so as not to change the behavior.
onAvailable(network, networkCapabilities, linkProperties, blocked != 0); onAvailable(network, networkCapabilities, linkProperties, blocked != 0);
if (null != localInfo) onLocalNetworkInfoChanged(network, localInfo);
onBlockedStatusChanged(network, blocked); onBlockedStatusChanged(network, blocked);
} }
@@ -3989,7 +3994,8 @@ public class ConnectivityManager {
*/ */
public void onAvailable(@NonNull Network network, public void onAvailable(@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities, @NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties, boolean blocked) { @NonNull LinkProperties linkProperties,
boolean blocked) {
onAvailable(network); onAvailable(network);
if (!networkCapabilities.hasCapability( if (!networkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)) { NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)) {
@@ -4115,6 +4121,19 @@ public class ConnectivityManager {
public void onLinkPropertiesChanged(@NonNull Network network, public void onLinkPropertiesChanged(@NonNull Network network,
@NonNull LinkProperties linkProperties) {} @NonNull LinkProperties linkProperties) {}
/**
* Called when there is a change in the {@link LocalNetworkInfo} for this network.
*
* This is only called for local networks, that is those with the
* NET_CAPABILITY_LOCAL_NETWORK network capability.
*
* @param network the {@link Network} whose local network info has changed.
* @param localNetworkInfo the new {@link LocalNetworkInfo} for this network.
* @hide
*/
public void onLocalNetworkInfoChanged(@NonNull Network network,
@NonNull LocalNetworkInfo localNetworkInfo) {}
/** /**
* Called when the network the framework connected to for this request suspends data * Called when the network the framework connected to for this request suspends data
* transmission temporarily. * transmission temporarily.
@@ -4209,27 +4228,29 @@ public class ConnectivityManager {
} }
/** @hide */ /** @hide */
public static final int CALLBACK_PRECHECK = 1; public static final int CALLBACK_PRECHECK = 1;
/** @hide */ /** @hide */
public static final int CALLBACK_AVAILABLE = 2; public static final int CALLBACK_AVAILABLE = 2;
/** @hide arg1 = TTL */ /** @hide arg1 = TTL */
public static final int CALLBACK_LOSING = 3; public static final int CALLBACK_LOSING = 3;
/** @hide */ /** @hide */
public static final int CALLBACK_LOST = 4; public static final int CALLBACK_LOST = 4;
/** @hide */ /** @hide */
public static final int CALLBACK_UNAVAIL = 5; public static final int CALLBACK_UNAVAIL = 5;
/** @hide */ /** @hide */
public static final int CALLBACK_CAP_CHANGED = 6; public static final int CALLBACK_CAP_CHANGED = 6;
/** @hide */ /** @hide */
public static final int CALLBACK_IP_CHANGED = 7; public static final int CALLBACK_IP_CHANGED = 7;
/** @hide obj = NetworkCapabilities, arg1 = seq number */ /** @hide obj = NetworkCapabilities, arg1 = seq number */
private static final int EXPIRE_LEGACY_REQUEST = 8; private static final int EXPIRE_LEGACY_REQUEST = 8;
/** @hide */ /** @hide */
public static final int CALLBACK_SUSPENDED = 9; public static final int CALLBACK_SUSPENDED = 9;
/** @hide */ /** @hide */
public static final int CALLBACK_RESUMED = 10; public static final int CALLBACK_RESUMED = 10;
/** @hide */ /** @hide */
public static final int CALLBACK_BLK_CHANGED = 11; public static final int CALLBACK_BLK_CHANGED = 11;
/** @hide */
public static final int CALLBACK_LOCAL_NETWORK_INFO_CHANGED = 12;
/** @hide */ /** @hide */
public static String getCallbackName(int whichCallback) { public static String getCallbackName(int whichCallback) {
@@ -4245,6 +4266,7 @@ public class ConnectivityManager {
case CALLBACK_SUSPENDED: return "CALLBACK_SUSPENDED"; case CALLBACK_SUSPENDED: return "CALLBACK_SUSPENDED";
case CALLBACK_RESUMED: return "CALLBACK_RESUMED"; case CALLBACK_RESUMED: return "CALLBACK_RESUMED";
case CALLBACK_BLK_CHANGED: return "CALLBACK_BLK_CHANGED"; case CALLBACK_BLK_CHANGED: return "CALLBACK_BLK_CHANGED";
case CALLBACK_LOCAL_NETWORK_INFO_CHANGED: return "CALLBACK_LOCAL_NETWORK_INFO_CHANGED";
default: default:
return Integer.toString(whichCallback); return Integer.toString(whichCallback);
} }
@@ -4299,7 +4321,8 @@ public class ConnectivityManager {
case CALLBACK_AVAILABLE: { case CALLBACK_AVAILABLE: {
NetworkCapabilities cap = getObject(message, NetworkCapabilities.class); NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
LinkProperties lp = getObject(message, LinkProperties.class); LinkProperties lp = getObject(message, LinkProperties.class);
callback.onAvailable(network, cap, lp, message.arg1); LocalNetworkInfo lni = getObject(message, LocalNetworkInfo.class);
callback.onAvailable(network, cap, lp, lni, message.arg1);
break; break;
} }
case CALLBACK_LOSING: { case CALLBACK_LOSING: {
@@ -4324,6 +4347,11 @@ public class ConnectivityManager {
callback.onLinkPropertiesChanged(network, lp); callback.onLinkPropertiesChanged(network, lp);
break; break;
} }
case CALLBACK_LOCAL_NETWORK_INFO_CHANGED: {
final LocalNetworkInfo info = getObject(message, LocalNetworkInfo.class);
callback.onLocalNetworkInfoChanged(network, info);
break;
}
case CALLBACK_SUSPENDED: { case CALLBACK_SUSPENDED: {
callback.onNetworkSuspended(network); callback.onNetworkSuspended(network);
break; break;

View File

@@ -0,0 +1,96 @@
/*
* 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 android.net;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Information about a local network.
*
* This is sent to ConnectivityManager.NetworkCallback.
* @hide
*/
// TODO : make public
public final class LocalNetworkInfo implements Parcelable {
@Nullable private final Network mUpstreamNetwork;
public LocalNetworkInfo(@Nullable final Network upstreamNetwork) {
this.mUpstreamNetwork = upstreamNetwork;
}
/**
* Return the upstream network, or null if none.
*/
@Nullable
public Network getUpstreamNetwork() {
return mUpstreamNetwork;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull final Parcel dest, final int flags) {
dest.writeParcelable(mUpstreamNetwork, flags);
}
@Override
public String toString() {
return "LocalNetworkInfo { upstream=" + mUpstreamNetwork + " }";
}
public static final @NonNull Creator<LocalNetworkInfo> CREATOR = new Creator<>() {
public LocalNetworkInfo createFromParcel(Parcel in) {
final Network upstreamNetwork = in.readParcelable(null);
return new LocalNetworkInfo(upstreamNetwork);
}
@Override
public LocalNetworkInfo[] newArray(final int size) {
return new LocalNetworkInfo[size];
}
};
/**
* Builder for LocalNetworkInfo
*/
public static final class Builder {
@Nullable private Network mUpstreamNetwork;
/**
* Set the upstream network, or null if none.
* @return the builder
*/
@NonNull public Builder setUpstreamNetwork(@Nullable final Network network) {
mUpstreamNetwork = network;
return this;
}
/**
* Build the LocalNetworkInfo
*/
@NonNull public LocalNetworkInfo build() {
return new LocalNetworkInfo(mUpstreamNetwork);
}
}
}

View File

@@ -168,6 +168,7 @@ import android.net.IpMemoryStore;
import android.net.IpPrefix; import android.net.IpPrefix;
import android.net.LinkProperties; import android.net.LinkProperties;
import android.net.LocalNetworkConfig; import android.net.LocalNetworkConfig;
import android.net.LocalNetworkInfo;
import android.net.MatchAllNetworkSpecifier; import android.net.MatchAllNetworkSpecifier;
import android.net.NativeNetworkConfig; import android.net.NativeNetworkConfig;
import android.net.NativeNetworkType; import android.net.NativeNetworkType;
@@ -4183,7 +4184,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
case NetworkAgent.EVENT_LOCAL_NETWORK_CONFIG_CHANGED: { case NetworkAgent.EVENT_LOCAL_NETWORK_CONFIG_CHANGED: {
final LocalNetworkConfig config = (LocalNetworkConfig) arg.second; final LocalNetworkConfig config = (LocalNetworkConfig) arg.second;
updateLocalNetworkConfig(nai, nai.localNetworkConfig, config); handleUpdateLocalNetworkConfig(nai, nai.localNetworkConfig, config);
break; break;
} }
case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: { case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: {
@@ -4946,7 +4947,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
notifyIfacesChangedForNetworkStats(); notifyIfacesChangedForNetworkStats();
// If this was a local network forwarded to some upstream, or if some local network was // 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. // forwarded to this nai, then disable forwarding rules now.
maybeDisableForwardRulesForDisconnectingNai(nai); maybeDisableForwardRulesForDisconnectingNai(nai, true /* sendCallbacks */);
// If this is a local network with an upstream selector, remove the associated network // If this is a local network with an upstream selector, remove the associated network
// request. // request.
if (nai.isLocalNetwork()) { if (nai.isLocalNetwork()) {
@@ -5069,7 +5070,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
private void maybeDisableForwardRulesForDisconnectingNai( private void maybeDisableForwardRulesForDisconnectingNai(
@NonNull final NetworkAgentInfo disconnecting) { @NonNull final NetworkAgentInfo disconnecting, final boolean sendCallbacks) {
// Step 1 : maybe this network was the upstream for one or more local networks. // Step 1 : maybe this network was the upstream for one or more local networks.
for (final NetworkAgentInfo local : mNetworkAgentInfos) { for (final NetworkAgentInfo local : mNetworkAgentInfos) {
if (!local.isLocalNetwork()) continue; if (!local.isLocalNetwork()) continue;
@@ -5082,6 +5083,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkAgentInfo satisfier = nri.getSatisfier(); final NetworkAgentInfo satisfier = nri.getSatisfier();
if (disconnecting != satisfier) continue; if (disconnecting != satisfier) continue;
removeLocalNetworkUpstream(local, disconnecting); removeLocalNetworkUpstream(local, disconnecting);
// Set the satisfier to null immediately so that the LOCAL_NETWORK_CHANGED callback
// correctly contains null as an upstream.
if (sendCallbacks) {
nri.setSatisfier(null, null);
notifyNetworkCallbacks(local,
ConnectivityManager.CALLBACK_LOCAL_NETWORK_INFO_CHANGED);
}
} }
// Step 2 : maybe this is a local network that had an upstream. // Step 2 : maybe this is a local network that had an upstream.
@@ -5148,8 +5156,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
mDscpPolicyTracker.removeAllDscpPolicies(nai, false); mDscpPolicyTracker.removeAllDscpPolicies(nai, false);
} }
// Remove any forwarding rules to and from the interface for this network, since // Remove any forwarding rules to and from the interface for this network, since
// the interface is going to go away. // the interface is going to go away. Don't send the callbacks however ; if the network
maybeDisableForwardRulesForDisconnectingNai(nai); // was is being disconnected the callbacks have already been sent, and if it is being
// destroyed pending replacement they will be sent when it is disconnected.
maybeDisableForwardRulesForDisconnectingNai(nai, false /* sendCallbacks */);
try { try {
mNetd.networkDestroy(nai.network.getNetId()); mNetd.networkDestroy(nai.network.getNetId());
} catch (RemoteException | ServiceSpecificException e) { } catch (RemoteException | ServiceSpecificException e) {
@@ -8320,7 +8330,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
if (nai.isLocalNetwork()) { if (nai.isLocalNetwork()) {
updateLocalNetworkConfig(nai, null /* oldConfig */, nai.localNetworkConfig); handleUpdateLocalNetworkConfig(nai, null /* oldConfig */, nai.localNetworkConfig);
} }
nai.notifyRegistered(); nai.notifyRegistered();
NetworkInfo networkInfo = nai.networkInfo; NetworkInfo networkInfo = nai.networkInfo;
@@ -8988,7 +8998,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
// oldConfig is null iff this is the original registration of the local network config // oldConfig is null iff this is the original registration of the local network config
private void updateLocalNetworkConfig(@NonNull final NetworkAgentInfo nai, private void handleUpdateLocalNetworkConfig(@NonNull final NetworkAgentInfo nai,
@Nullable final LocalNetworkConfig oldConfig, @Nullable final LocalNetworkConfig oldConfig,
@NonNull final LocalNetworkConfig newConfig) { @NonNull final LocalNetworkConfig newConfig) {
if (!nai.isLocalNetwork()) { if (!nai.isLocalNetwork()) {
@@ -9021,6 +9031,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
// If there is an old satisfier, but no new request, then remove the old upstream. // If there is an old satisfier, but no new request, then remove the old upstream.
removeLocalNetworkUpstream(nai, oldSatisfier); removeLocalNetworkUpstream(nai, oldSatisfier);
nai.localNetworkConfig = configBuilder.build(); nai.localNetworkConfig = configBuilder.build();
// When there is a new request, the rematch sees the new request and sends the
// LOCAL_NETWORK_INFO_CHANGED callbacks accordingly.
// But here there is no new request, so the rematch won't see anything. Send
// callbacks to apps now to tell them about the loss of upstream.
notifyNetworkCallbacks(nai,
ConnectivityManager.CALLBACK_LOCAL_NETWORK_INFO_CHANGED);
return; return;
} }
} }
@@ -9042,12 +9058,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
nri.setSatisfier(oldSatisfier, nr); nri.setSatisfier(oldSatisfier, nr);
} }
nai.localNetworkConfig = configBuilder.build(); nai.localNetworkConfig = configBuilder.build();
// handleRegisterNetworkRequest causes a rematch. The rematch must happen after
// nai.localNetworkConfig is set, since it will base its callbacks on the old
// satisfier and the new request.
handleRegisterNetworkRequest(nri); handleRegisterNetworkRequest(nri);
} else { } else {
configBuilder.setUpstreamSelector(oldRequest); configBuilder.setUpstreamSelector(oldRequest);
nai.localNetworkConfig = configBuilder.build(); nai.localNetworkConfig = configBuilder.build();
} }
} }
/** /**
@@ -9378,6 +9396,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
releasePendingNetworkRequestWithDelay(pendingIntent); releasePendingNetworkRequestWithDelay(pendingIntent);
} }
@Nullable
private LocalNetworkInfo localNetworkInfoForNai(@NonNull final NetworkAgentInfo nai) {
if (!nai.isLocalNetwork()) return null;
final Network upstream;
final NetworkRequest selector = nai.localNetworkConfig.getUpstreamSelector();
if (null == selector) {
upstream = null;
} else {
final NetworkRequestInfo upstreamNri = mNetworkRequests.get(selector);
final NetworkAgentInfo satisfier = upstreamNri.getSatisfier();
upstream = (null == satisfier) ? null : satisfier.network;
}
return new LocalNetworkInfo.Builder().setUpstreamNetwork(upstream).build();
}
// networkAgent is only allowed to be null if notificationType is // networkAgent is only allowed to be null if notificationType is
// CALLBACK_UNAVAIL. This is because UNAVAIL is about no network being // CALLBACK_UNAVAIL. This is because UNAVAIL is about no network being
// available, while all other cases are about some particular network. // available, while all other cases are about some particular network.
@@ -9413,6 +9446,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
putParcelable(bundle, nc); putParcelable(bundle, nc);
putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions( putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
networkAgent.linkProperties, nri.mPid, nri.mUid)); networkAgent.linkProperties, nri.mPid, nri.mUid));
// The local network info is often null, so can't use the static putParcelable
// method here.
bundle.putParcelable(LocalNetworkInfo.class.getSimpleName(),
localNetworkInfoForNai(networkAgent));
// For this notification, arg1 contains the blocked status. // For this notification, arg1 contains the blocked status.
msg.arg1 = arg1; msg.arg1 = arg1;
break; break;
@@ -9444,6 +9481,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
msg.arg1 = arg1; msg.arg1 = arg1;
break; break;
} }
case ConnectivityManager.CALLBACK_LOCAL_NETWORK_INFO_CHANGED: {
if (!networkAgent.isLocalNetwork()) {
Log.wtf(TAG, "Callback for local info for a non-local network");
return;
}
putParcelable(bundle, localNetworkInfoForNai(networkAgent));
break;
}
} }
msg.what = notificationType; msg.what = notificationType;
msg.setData(bundle); msg.setData(bundle);
@@ -10077,6 +10122,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
loge("Can't update forwarding rules", e); loge("Can't update forwarding rules", e);
} }
} }
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOCAL_NETWORK_INFO_CHANGED);
} }
updateLegacyTypeTrackerAndVpnLockdownForRematch(changes, nais); updateLegacyTypeTrackerAndVpnLockdownForRematch(changes, nais);

View File

@@ -18,6 +18,7 @@ package com.android.testutils
import android.net.ConnectivityManager.NetworkCallback import android.net.ConnectivityManager.NetworkCallback
import android.net.LinkProperties import android.net.LinkProperties
import android.net.LocalNetworkInfo
import android.net.Network import android.net.Network
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
@@ -28,6 +29,7 @@ import com.android.testutils.RecorderCallback.CallbackEntry.BlockedStatus
import com.android.testutils.RecorderCallback.CallbackEntry.BlockedStatusInt import com.android.testutils.RecorderCallback.CallbackEntry.BlockedStatusInt
import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.LocalInfoChanged
import com.android.testutils.RecorderCallback.CallbackEntry.Losing import com.android.testutils.RecorderCallback.CallbackEntry.Losing
import com.android.testutils.RecorderCallback.CallbackEntry.Lost import com.android.testutils.RecorderCallback.CallbackEntry.Lost
import com.android.testutils.RecorderCallback.CallbackEntry.Resumed import com.android.testutils.RecorderCallback.CallbackEntry.Resumed
@@ -68,6 +70,10 @@ open class RecorderCallback private constructor(
override val network: Network, override val network: Network,
val lp: LinkProperties val lp: LinkProperties
) : CallbackEntry() ) : CallbackEntry()
data class LocalInfoChanged(
override val network: Network,
val info: LocalNetworkInfo
) : CallbackEntry()
data class Suspended(override val network: Network) : CallbackEntry() data class Suspended(override val network: Network) : CallbackEntry()
data class Resumed(override val network: Network) : CallbackEntry() data class Resumed(override val network: Network) : CallbackEntry()
data class Losing(override val network: Network, val maxMsToLive: Int) : CallbackEntry() data class Losing(override val network: Network, val maxMsToLive: Int) : CallbackEntry()
@@ -94,6 +100,8 @@ open class RecorderCallback private constructor(
@JvmField @JvmField
val LINK_PROPERTIES_CHANGED = LinkPropertiesChanged::class val LINK_PROPERTIES_CHANGED = LinkPropertiesChanged::class
@JvmField @JvmField
val LOCAL_INFO_CHANGED = LocalInfoChanged::class
@JvmField
val SUSPENDED = Suspended::class val SUSPENDED = Suspended::class
@JvmField @JvmField
val RESUMED = Resumed::class val RESUMED = Resumed::class
@@ -131,6 +139,11 @@ open class RecorderCallback private constructor(
history.add(LinkPropertiesChanged(network, lp)) history.add(LinkPropertiesChanged(network, lp))
} }
override fun onLocalNetworkInfoChanged(network: Network, info: LocalNetworkInfo) {
Log.d(TAG, "onLocalNetworkInfoChanged $network $info")
history.add(LocalInfoChanged(network, info))
}
override fun onBlockedStatusChanged(network: Network, blocked: Boolean) { override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
Log.d(TAG, "onBlockedStatusChanged $network $blocked") Log.d(TAG, "onBlockedStatusChanged $network $blocked")
history.add(BlockedStatus(network, blocked)) history.add(BlockedStatus(network, blocked))
@@ -430,37 +443,63 @@ open class TestableNetworkCallback private constructor(
suspended: Boolean = false, suspended: Boolean = false,
validated: Boolean? = true, validated: Boolean? = true,
blocked: Boolean = false, blocked: Boolean = false,
upstream: Network? = null,
tmt: Long = defaultTimeoutMs tmt: Long = defaultTimeoutMs
) { ) {
expectAvailableCallbacksCommon(net, suspended, validated, tmt) expectAvailableCallbacksCommon(net, suspended, validated, upstream, tmt)
expect<BlockedStatus>(net, tmt) { it.blocked == blocked } expect<BlockedStatus>(net, tmt) { it.blocked == blocked }
} }
// For backward compatibility, add a method that allows callers to specify a timeout but
// no upstream.
fun expectAvailableCallbacks(
net: Network,
suspended: Boolean = false,
validated: Boolean? = true,
blocked: Boolean = false,
tmt: Long = defaultTimeoutMs
) = expectAvailableCallbacks(net, suspended, validated, blocked, upstream = null, tmt = tmt)
fun expectAvailableCallbacks( fun expectAvailableCallbacks(
net: Network, net: Network,
suspended: Boolean, suspended: Boolean,
validated: Boolean, validated: Boolean,
blockedReason: Int, blockedReason: Int,
upstream: Network? = null,
tmt: Long tmt: Long
) { ) {
expectAvailableCallbacksCommon(net, suspended, validated, tmt) expectAvailableCallbacksCommon(net, suspended, validated, upstream, tmt)
expect<BlockedStatusInt>(net) { it.reason == blockedReason } expect<BlockedStatusInt>(net) { it.reason == blockedReason }
} }
// For backward compatibility, add a method that allows callers to specify a timeout but
// no upstream.
fun expectAvailableCallbacks(
net: Network,
suspended: Boolean = false,
validated: Boolean = true,
blockedReason: Int,
tmt: Long = defaultTimeoutMs
) = expectAvailableCallbacks(net, suspended, validated, blockedReason, upstream = null, tmt)
private fun expectAvailableCallbacksCommon( private fun expectAvailableCallbacksCommon(
net: Network, net: Network,
suspended: Boolean, suspended: Boolean,
validated: Boolean?, validated: Boolean?,
upstream: Network?,
tmt: Long tmt: Long
) { ) {
expect<Available>(net, tmt) expect<Available>(net, tmt)
if (suspended) { if (suspended) {
expect<Suspended>(net, tmt) expect<Suspended>(net, tmt)
} }
expect<CapabilitiesChanged>(net, tmt) { val caps = expect<CapabilitiesChanged>(net, tmt) {
validated == null || validated == it.caps.hasCapability(NET_CAPABILITY_VALIDATED) validated == null || validated == it.caps.hasCapability(NET_CAPABILITY_VALIDATED)
} }.caps
expect<LinkPropertiesChanged>(net, tmt) expect<LinkPropertiesChanged>(net, tmt)
if (caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK)) {
expect<LocalInfoChanged>(net, tmt) { it.info.upstreamNetwork == upstream }
}
} }
// Backward compatibility for existing Java code. Use named arguments instead and remove all // Backward compatibility for existing Java code. Use named arguments instead and remove all
@@ -507,13 +546,15 @@ open class TestableNetworkCallback private constructor(
val network: Network val network: Network
} }
@JvmOverloads
fun expectAvailableCallbacks( fun expectAvailableCallbacks(
n: HasNetwork, n: HasNetwork,
suspended: Boolean, suspended: Boolean,
validated: Boolean, validated: Boolean,
blocked: Boolean, blocked: Boolean,
upstream: Network? = null,
timeoutMs: Long timeoutMs: Long
) = expectAvailableCallbacks(n.network, suspended, validated, blocked, timeoutMs) ) = expectAvailableCallbacks(n.network, suspended, validated, blocked, upstream, timeoutMs)
fun expectAvailableAndSuspendedCallbacks(n: HasNetwork, expectValidated: Boolean) { fun expectAvailableAndSuspendedCallbacks(n: HasNetwork, expectValidated: Boolean) {
expectAvailableAndSuspendedCallbacks(n.network, expectValidated) expectAvailableAndSuspendedCallbacks(n.network, expectValidated)

View File

@@ -38,10 +38,7 @@ import android.net.RouteInfo
import android.os.Build import android.os.Build
import com.android.testutils.DevSdkIgnoreRule import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.RecorderCallback.CallbackEntry.Available import com.android.testutils.RecorderCallback.CallbackEntry.LocalInfoChanged
import com.android.testutils.RecorderCallback.CallbackEntry.BlockedStatus
import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.RecorderCallback.CallbackEntry.Lost import com.android.testutils.RecorderCallback.CallbackEntry.Lost
import com.android.testutils.TestableNetworkCallback import com.android.testutils.TestableNetworkCallback
import org.junit.Test import org.junit.Test
@@ -114,10 +111,7 @@ class CSLocalAgentTests : CSTest() {
.build(), .build(),
lnc = LocalNetworkConfig.Builder().build()) lnc = LocalNetworkConfig.Builder().build())
agent.connect() agent.connect()
cb.expect<Available>(agent.network) cb.expectAvailableCallbacks(agent.network, validated = false)
cb.expect<CapabilitiesChanged>(agent.network)
cb.expect<LinkPropertiesChanged>(agent.network)
cb.expect<BlockedStatus>(agent.network)
agent.sendNetworkCapabilities(NetworkCapabilities.Builder().build()) agent.sendNetworkCapabilities(NetworkCapabilities.Builder().build())
cb.expect<Lost>(agent.network) cb.expect<Lost>(agent.network)
@@ -125,10 +119,7 @@ class CSLocalAgentTests : CSTest() {
.build(), .build(),
lnc = null) lnc = null)
agent2.connect() agent2.connect()
cb.expect<Available>(agent2.network) cb.expectAvailableCallbacks(agent2.network, validated = false)
cb.expect<CapabilitiesChanged>(agent2.network)
cb.expect<LinkPropertiesChanged>(agent2.network)
cb.expect<BlockedStatus>(agent2.network)
agent2.sendNetworkCapabilities(NetworkCapabilities.Builder() agent2.sendNetworkCapabilities(NetworkCapabilities.Builder()
.addCapability(NET_CAPABILITY_LOCAL_NETWORK) .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
.build()) .build())
@@ -153,10 +144,11 @@ class CSLocalAgentTests : CSTest() {
) )
localAgent.connect() localAgent.connect()
cb.expect<Available>(localAgent.network) cb.expectAvailableCallbacks(localAgent.network, validated = false)
cb.expect<CapabilitiesChanged>(localAgent.network)
cb.expect<LinkPropertiesChanged>(localAgent.network) val wifiAgent = Agent(score = keepScore(), lp = lp("wifi0"),
cb.expect<BlockedStatus>(localAgent.network) nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
wifiAgent.connect()
val newLnc = LocalNetworkConfig.Builder() val newLnc = LocalNetworkConfig.Builder()
.setUpstreamSelector(NetworkRequest.Builder() .setUpstreamSelector(NetworkRequest.Builder()
@@ -165,6 +157,21 @@ class CSLocalAgentTests : CSTest() {
.build() .build()
localAgent.sendLocalNetworkConfig(newLnc) localAgent.sendLocalNetworkConfig(newLnc)
cb.expect<LocalInfoChanged>(localAgent.network) {
it.info.upstreamNetwork == wifiAgent.network
}
localAgent.sendLocalNetworkConfig(LocalNetworkConfig.Builder().build())
cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
localAgent.sendLocalNetworkConfig(newLnc)
cb.expect<LocalInfoChanged>(localAgent.network) {
it.info.upstreamNetwork == wifiAgent.network
}
wifiAgent.disconnect()
cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
localAgent.disconnect() localAgent.disconnect()
} }
@@ -204,6 +211,9 @@ class CSLocalAgentTests : CSTest() {
wifiAgent.connect() wifiAgent.connect()
cb.expectAvailableCallbacks(wifiAgent.network, validated = false) cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
cb.expect<LocalInfoChanged>(localAgent.network) {
it.info.upstreamNetwork == wifiAgent.network
}
clearInvocations(netd) clearInvocations(netd)
val inOrder = inOrder(netd) val inOrder = inOrder(netd)
@@ -218,6 +228,7 @@ class CSLocalAgentTests : CSTest() {
wifiAgent2.connect() wifiAgent2.connect()
cb.expectAvailableCallbacks(wifiAgent2.network, validated = false) cb.expectAvailableCallbacks(wifiAgent2.network, validated = false)
cb.expect<LocalInfoChanged> { it.info.upstreamNetwork == wifiAgent2.network }
cb.expect<Lost> { it.network == wifiAgent.network } cb.expect<Lost> { it.network == wifiAgent.network }
inOrder.verify(netd).ipfwdAddInterfaceForward("local0", wifiIface2) inOrder.verify(netd).ipfwdAddInterfaceForward("local0", wifiIface2)
@@ -252,7 +263,10 @@ class CSLocalAgentTests : CSTest() {
nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)) nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
wifiAgent.connect() wifiAgent.connect()
cb.expectAvailableCallbacksUnvalidated(wifiAgent) cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
cb.expect<LocalInfoChanged>(localAgent.network) {
it.info.upstreamNetwork == wifiAgent.network
}
clearInvocations(netd) clearInvocations(netd)
wifiAgent.unregisterAfterReplacement(TIMEOUT_MS.toInt()) wifiAgent.unregisterAfterReplacement(TIMEOUT_MS.toInt())
@@ -260,6 +274,7 @@ class CSLocalAgentTests : CSTest() {
verify(netd).networkDestroy(wifiAgent.network.netId) verify(netd).networkDestroy(wifiAgent.network.netId)
verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi0") verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi0")
cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
cb.expect<Lost> { it.network == wifiAgent.network } cb.expect<Lost> { it.network == wifiAgent.network }
} }
@@ -294,7 +309,12 @@ class CSLocalAgentTests : CSTest() {
val wifiAgent = Agent(lp = lp("wifi0"), nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)) val wifiAgent = Agent(lp = lp("wifi0"), nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
wifiAgent.connect() wifiAgent.connect()
cb.expectAvailableCallbacksUnvalidated(wifiAgent) cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
listOf(cb, localCb).forEach {
it.expect<LocalInfoChanged>(localAgent.network) {
it.info.upstreamNetwork == wifiAgent.network
}
}
verify(netd).ipfwdAddInterfaceForward("local0", "wifi0") verify(netd).ipfwdAddInterfaceForward("local0", "wifi0")
@@ -303,8 +323,10 @@ class CSLocalAgentTests : CSTest() {
val localAgent2 = Agent(nc = localNc, lp = lp("local0"), lnc = lnc, score = localScore) val localAgent2 = Agent(nc = localNc, lp = lp("local0"), lnc = lnc, score = localScore)
localAgent2.connect() localAgent2.connect()
localCb.expectAvailableCallbacks(localAgent2.network, validated = false) localCb.expectAvailableCallbacks(localAgent2.network,
cb.expectAvailableCallbacks(localAgent2.network, validated = false) validated = false, upstream = wifiAgent.network)
cb.expectAvailableCallbacks(localAgent2.network,
validated = false, upstream = wifiAgent.network)
cb.expect<Lost> { it.network == localAgent.network } cb.expect<Lost> { it.network == localAgent.network }
} }
@@ -316,9 +338,11 @@ class CSLocalAgentTests : CSTest() {
val wifiAgent = Agent(lp = lp("wifi0"), nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)) val wifiAgent = Agent(lp = lp("wifi0"), nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
wifiAgent.connect() wifiAgent.connect()
cb.expectAvailableCallbacksUnvalidated(wifiAgent) cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
// Set up a local agent that should forward its traffic to the best wifi upstream. // Unregister wifi pending replacement, then set up a local agent that would have
// this network as its upstream.
wifiAgent.unregisterAfterReplacement(LONG_TIMEOUT_MS)
val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK), val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
lp = lp("local0"), lp = lp("local0"),
lnc = LocalNetworkConfig.Builder() lnc = LocalNetworkConfig.Builder()
@@ -331,14 +355,18 @@ class CSLocalAgentTests : CSTest() {
.build()) .build())
) )
// ...but destroy the wifi agent before connecting it // Connect the local agent. The zombie wifi is its upstream, but the stack doesn't
wifiAgent.unregisterAfterReplacement(LONG_TIMEOUT_MS) // tell netd to add the forward since the wifi0 interface has gone.
localAgent.connect() localAgent.connect()
cb.expectAvailableCallbacks(localAgent.network, validated = false) cb.expectAvailableCallbacks(localAgent.network,
validated = false, upstream = wifiAgent.network)
verify(netd).ipfwdAddInterfaceForward("local0", "wifi0") verify(netd, never()).ipfwdAddInterfaceForward("local0", "wifi0")
verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi0")
// Disconnect wifi without a replacement. Expect an update with upstream null.
wifiAgent.disconnect()
verify(netd, never()).ipfwdAddInterfaceForward("local0", "wifi0")
cb.expect<LocalInfoChanged> { it.info.upstreamNetwork == null }
} }
@Test @Test
@@ -366,19 +394,34 @@ class CSLocalAgentTests : CSTest() {
val wifiAgentDun = Agent(score = keepScore(), lp = lp("wifi1"), val wifiAgentDun = Agent(score = keepScore(), lp = lp("wifi1"),
nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN)) nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
val cb = TestableNetworkCallback()
cm.registerNetworkCallback(NetworkRequest.Builder()
.addCapability(NET_CAPABILITY_LOCAL_NETWORK)
.build(),
cb)
cb.expectAvailableCallbacks(localAgent.network, validated = false)
val inOrder = inOrder(netd) val inOrder = inOrder(netd)
inOrder.verify(netd, never()).ipfwdAddInterfaceForward(any(), any()) inOrder.verify(netd, never()).ipfwdAddInterfaceForward(any(), any())
cb.assertNoCallback()
wifiAgent.connect() wifiAgent.connect()
inOrder.verify(netd, never()).ipfwdAddInterfaceForward(any(), any()) inOrder.verify(netd, never()).ipfwdAddInterfaceForward(any(), any())
cb.assertNoCallback()
cellAgentDun.connect() cellAgentDun.connect()
inOrder.verify(netd).ipfwdEnableForwarding(any()) inOrder.verify(netd).ipfwdEnableForwarding(any())
inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "cell0") inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "cell0")
cb.expect<LocalInfoChanged>(localAgent.network) {
it.info.upstreamNetwork == cellAgentDun.network
}
wifiAgentDun.connect() wifiAgentDun.connect()
inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "cell0") inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "cell0")
inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "wifi1") inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "wifi1")
cb.expect<LocalInfoChanged>(localAgent.network) {
it.info.upstreamNetwork == wifiAgentDun.network
}
// Make sure sending the same config again doesn't do anything // Make sure sending the same config again doesn't do anything
repeat(5) { repeat(5) {
@@ -387,6 +430,10 @@ class CSLocalAgentTests : CSTest() {
inOrder.verifyNoMoreInteractions() inOrder.verifyNoMoreInteractions()
wifiAgentDun.disconnect() wifiAgentDun.disconnect()
cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
cb.expect<LocalInfoChanged>(localAgent.network) {
it.info.upstreamNetwork == cellAgentDun.network
}
inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi1") inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi1")
// This can take a little bit of time because it needs to wait for the rematch // This can take a little bit of time because it needs to wait for the rematch
inOrder.verify(netd, timeout(MEDIUM_TIMEOUT_MS)).ipfwdAddInterfaceForward("local0", "cell0") inOrder.verify(netd, timeout(MEDIUM_TIMEOUT_MS)).ipfwdAddInterfaceForward("local0", "cell0")
@@ -394,15 +441,35 @@ class CSLocalAgentTests : CSTest() {
cellAgentDun.disconnect() cellAgentDun.disconnect()
inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "cell0") inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "cell0")
inOrder.verify(netd).ipfwdDisableForwarding(any()) inOrder.verify(netd).ipfwdDisableForwarding(any())
cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
val wifiAgentDun2 = Agent(score = keepScore(), lp = lp("wifi2"), val wifiAgentDun2 = Agent(score = keepScore(), lp = lp("wifi2"),
nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN)) nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
wifiAgentDun2.connect() wifiAgentDun2.connect()
inOrder.verify(netd).ipfwdEnableForwarding(any()) inOrder.verify(netd).ipfwdEnableForwarding(any())
inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "wifi2") inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "wifi2")
cb.expect<LocalInfoChanged>(localAgent.network) {
it.info.upstreamNetwork == wifiAgentDun2.network
}
localAgent.disconnect() wifiAgentDun2.disconnect()
inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi2") inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi2")
inOrder.verify(netd).ipfwdDisableForwarding(any()) inOrder.verify(netd).ipfwdDisableForwarding(any())
cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
val wifiAgentDun3 = Agent(score = keepScore(), lp = lp("wifi3"),
nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
wifiAgentDun3.connect()
inOrder.verify(netd).ipfwdEnableForwarding(any())
inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "wifi3")
cb.expect<LocalInfoChanged>(localAgent.network) {
it.info.upstreamNetwork == wifiAgentDun3.network
}
localAgent.disconnect()
inOrder.verify(netd).ipfwdRemoveInterfaceForward("local0", "wifi3")
inOrder.verify(netd).ipfwdDisableForwarding(any())
cb.expect<Lost>(localAgent.network)
cb.assertNoCallback()
} }
} }