diff --git a/framework/aidl-export/android/net/LocalNetworkConfig.aidl b/framework/aidl-export/android/net/LocalNetworkConfig.aidl new file mode 100644 index 0000000000..e2829a5a41 --- /dev/null +++ b/framework/aidl-export/android/net/LocalNetworkConfig.aidl @@ -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; + +@JavaOnlyStableParcelable parcelable LocalNetworkConfig; diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java index 23155210be..915c20da5f 100644 --- a/framework/src/android/net/ConnectivityManager.java +++ b/framework/src/android/net/ConnectivityManager.java @@ -3811,11 +3811,28 @@ public class ConnectivityManager { @RequiresPermission(anyOf = { NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) - public Network registerNetworkAgent(INetworkAgent na, NetworkInfo ni, LinkProperties lp, - NetworkCapabilities nc, @NonNull NetworkScore score, NetworkAgentConfig config, - int providerId) { + public Network registerNetworkAgent(@NonNull INetworkAgent na, @NonNull NetworkInfo ni, + @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, + @NonNull NetworkScore score, @NonNull NetworkAgentConfig config, int providerId) { + return registerNetworkAgent(na, ni, lp, nc, null /* localNetworkConfig */, score, config, + providerId); + } + + /** + * @hide + * Register a NetworkAgent with ConnectivityService. + * @return Network corresponding to NetworkAgent. + */ + @RequiresPermission(anyOf = { + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + android.Manifest.permission.NETWORK_FACTORY}) + public Network registerNetworkAgent(@NonNull INetworkAgent na, @NonNull NetworkInfo ni, + @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, + @Nullable LocalNetworkConfig localNetworkConfig, @NonNull NetworkScore score, + @NonNull NetworkAgentConfig config, int providerId) { try { - return mService.registerNetworkAgent(na, ni, lp, nc, score, config, providerId); + return mService.registerNetworkAgent(na, ni, lp, nc, score, localNetworkConfig, config, + providerId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl index ebe8bca77e..fe277732b0 100644 --- a/framework/src/android/net/IConnectivityManager.aidl +++ b/framework/src/android/net/IConnectivityManager.aidl @@ -27,6 +27,7 @@ import android.net.INetworkOfferCallback; import android.net.IQosCallback; import android.net.ISocketKeepaliveCallback; import android.net.LinkProperties; +import android.net.LocalNetworkConfig; import android.net.Network; import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; @@ -146,7 +147,8 @@ interface IConnectivityManager void declareNetworkRequestUnfulfillable(in NetworkRequest request); Network registerNetworkAgent(in INetworkAgent na, in NetworkInfo ni, in LinkProperties lp, - in NetworkCapabilities nc, in NetworkScore score, in NetworkAgentConfig config, + in NetworkCapabilities nc, in NetworkScore score, + in LocalNetworkConfig localNetworkConfig, in NetworkAgentConfig config, in int factorySerialNumber); NetworkRequest requestNetwork(int uid, in NetworkCapabilities networkCapabilities, int reqType, diff --git a/framework/src/android/net/INetworkAgentRegistry.aidl b/framework/src/android/net/INetworkAgentRegistry.aidl index b375b7b649..61b27b5f0e 100644 --- a/framework/src/android/net/INetworkAgentRegistry.aidl +++ b/framework/src/android/net/INetworkAgentRegistry.aidl @@ -17,6 +17,7 @@ package android.net; import android.net.DscpPolicy; import android.net.LinkProperties; +import android.net.LocalNetworkConfig; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; @@ -34,6 +35,7 @@ oneway interface INetworkAgentRegistry { void sendLinkProperties(in LinkProperties lp); // TODO: consider replacing this by "markConnected()" and removing void sendNetworkInfo(in NetworkInfo info); + void sendLocalNetworkConfig(in LocalNetworkConfig config); void sendScore(in NetworkScore score); void sendExplicitlySelected(boolean explicitlySelected, boolean acceptPartial); void sendSocketKeepaliveEvent(int slot, int reason); diff --git a/framework/src/android/net/LocalNetworkConfig.java b/framework/src/android/net/LocalNetworkConfig.java new file mode 100644 index 0000000000..fca7fd125a --- /dev/null +++ b/framework/src/android/net/LocalNetworkConfig.java @@ -0,0 +1,168 @@ +/* + * 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; + +/** + * A class to communicate configuration info about a local network through {@link NetworkAgent}. + * @hide + */ +// TODO : @SystemApi +public final class LocalNetworkConfig implements Parcelable { + @Nullable + private final NetworkRequest mUpstreamSelector; + + @NonNull + private final MulticastRoutingConfig mUpstreamMulticastRoutingConfig; + + @NonNull + private final MulticastRoutingConfig mDownstreamMulticastRoutingConfig; + + private LocalNetworkConfig(@Nullable final NetworkRequest upstreamSelector, + @Nullable final MulticastRoutingConfig upstreamConfig, + @Nullable final MulticastRoutingConfig downstreamConfig) { + mUpstreamSelector = upstreamSelector; + if (null != upstreamConfig) { + mUpstreamMulticastRoutingConfig = upstreamConfig; + } else { + mUpstreamMulticastRoutingConfig = MulticastRoutingConfig.CONFIG_FORWARD_NONE; + } + if (null != downstreamConfig) { + mDownstreamMulticastRoutingConfig = downstreamConfig; + } else { + mDownstreamMulticastRoutingConfig = MulticastRoutingConfig.CONFIG_FORWARD_NONE; + } + } + + /** + * Get the request choosing which network traffic from this network is forwarded to and from. + * + * This may be null if the local network doesn't forward the traffic anywhere. + */ + @Nullable + public NetworkRequest getUpstreamSelector() { + return mUpstreamSelector; + } + + public @NonNull MulticastRoutingConfig getUpstreamMulticastRoutingConfig() { + return mUpstreamMulticastRoutingConfig; + } + + public @NonNull MulticastRoutingConfig getDownstreamMulticastRoutingConfig() { + return mDownstreamMulticastRoutingConfig; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull final Parcel dest, final int flags) { + dest.writeParcelable(mUpstreamSelector, flags); + dest.writeParcelable(mUpstreamMulticastRoutingConfig, flags); + dest.writeParcelable(mDownstreamMulticastRoutingConfig, flags); + } + + public static final @NonNull Creator CREATOR = new Creator<>() { + public LocalNetworkConfig createFromParcel(Parcel in) { + final NetworkRequest upstreamSelector = in.readParcelable(null); + final MulticastRoutingConfig upstreamConfig = in.readParcelable(null); + final MulticastRoutingConfig downstreamConfig = in.readParcelable(null); + return new LocalNetworkConfig( + upstreamSelector, upstreamConfig, downstreamConfig); + } + + @Override + public LocalNetworkConfig[] newArray(final int size) { + return new LocalNetworkConfig[size]; + } + }; + + + public static final class Builder { + @Nullable + NetworkRequest mUpstreamSelector; + + @Nullable + MulticastRoutingConfig mUpstreamMulticastRoutingConfig; + + @Nullable + MulticastRoutingConfig mDownstreamMulticastRoutingConfig; + + /** + * Create a Builder + */ + public Builder() { + } + + /** + * Set to choose where this local network should forward its traffic to. + * + * The system will automatically choose the best network matching the request as an + * upstream, and set up forwarding between this local network and the chosen upstream. + * If no network matches the request, there is no upstream and the traffic is not forwarded. + * The caller can know when this changes by listening to link properties changes of + * this network with the {@link android.net.LinkProperties#getForwardedNetwork()} getter. + * + * Set this to null if the local network shouldn't be forwarded. Default is null. + */ + @NonNull + public Builder setUpstreamSelector(@Nullable NetworkRequest upstreamSelector) { + mUpstreamSelector = upstreamSelector; + return this; + } + + /** + * Set the upstream multicast routing config. + * + * If null, don't route multicast packets upstream. This is equivalent to a + * MulticastRoutingConfig in mode FORWARD_NONE. The default is null. + */ + @NonNull + public Builder setUpstreamMulticastRoutingConfig(@Nullable MulticastRoutingConfig cfg) { + mUpstreamMulticastRoutingConfig = cfg; + return this; + } + + /** + * Set the downstream multicast routing config. + * + * If null, don't route multicast packets downstream. This is equivalent to a + * MulticastRoutingConfig in mode FORWARD_NONE. The default is null. + */ + @NonNull + public Builder setDownstreamMulticastRoutingConfig(@Nullable MulticastRoutingConfig cfg) { + mDownstreamMulticastRoutingConfig = cfg; + return this; + } + + /** + * Build the LocalNetworkConfig object. + */ + @NonNull + public LocalNetworkConfig build() { + return new LocalNetworkConfig(mUpstreamSelector, + mUpstreamMulticastRoutingConfig, + mDownstreamMulticastRoutingConfig); + } + } +} diff --git a/framework/src/android/net/MulticastRoutingConfig.java b/framework/src/android/net/MulticastRoutingConfig.java new file mode 100644 index 0000000000..ebd9fc58c7 --- /dev/null +++ b/framework/src/android/net/MulticastRoutingConfig.java @@ -0,0 +1,264 @@ +/* + * 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.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArraySet; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.net.Inet6Address; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; + +/** + * A class representing a configuration for multicast routing. + * + * Internal usage to Connectivity + * @hide + */ +// TODO : @SystemApi +public class MulticastRoutingConfig implements Parcelable { + private static final String TAG = MulticastRoutingConfig.class.getSimpleName(); + + /** Do not forward any multicast packets. */ + public static final int FORWARD_NONE = 0; + /** + * Forward only multicast packets with destination in the list of listening addresses. + * Ignore the min scope. + */ + public static final int FORWARD_SELECTED = 1; + /** + * Forward all multicast packets with scope greater or equal than the min scope. + * Ignore the list of listening addresses. + */ + public static final int FORWARD_WITH_MIN_SCOPE = 2; + + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "FORWARD_" }, value = { + FORWARD_NONE, + FORWARD_SELECTED, + FORWARD_WITH_MIN_SCOPE + }) + public @interface MulticastForwardingMode {} + + /** + * Not a multicast scope, for configurations that do not use the min scope. + */ + public static final int MULTICAST_SCOPE_NONE = -1; + + public static final MulticastRoutingConfig CONFIG_FORWARD_NONE = + new MulticastRoutingConfig(FORWARD_NONE, MULTICAST_SCOPE_NONE, null); + + @MulticastForwardingMode + private final int mForwardingMode; + + private final int mMinScope; + + @NonNull + private final Set mListeningAddresses; + + private MulticastRoutingConfig(@MulticastForwardingMode final int mode, final int scope, + @Nullable final Set addresses) { + mForwardingMode = mode; + mMinScope = scope; + if (null != addresses) { + mListeningAddresses = Collections.unmodifiableSet(new ArraySet<>(addresses)); + } else { + mListeningAddresses = Collections.emptySet(); + } + } + + /** + * Returns the forwarding mode. + */ + @MulticastForwardingMode + public int getForwardingMode() { + return mForwardingMode; + } + + /** + * Returns the minimal group address scope that is allowed for forwarding. + * If the forwarding mode is not FORWARD_WITH_MIN_SCOPE, will be MULTICAST_SCOPE_NONE. + */ + public int getMinScope() { + return mMinScope; + } + + /** + * Returns the list of group addresses listened by the outgoing interface. + * The list will be empty if the forwarding mode is not FORWARD_SELECTED. + */ + @NonNull + public Set getMulticastListeningAddresses() { + return mListeningAddresses; + } + + private MulticastRoutingConfig(Parcel in) { + mForwardingMode = in.readInt(); + mMinScope = in.readInt(); + final int count = in.readInt(); + final ArraySet listeningAddresses = new ArraySet<>(count); + final byte[] buffer = new byte[16]; // Size of an Inet6Address + for (int i = 0; i < count; ++i) { + in.readByteArray(buffer); + try { + listeningAddresses.add((Inet6Address) Inet6Address.getByAddress(buffer)); + } catch (UnknownHostException e) { + Log.wtf(TAG, "Can't read inet6address : " + Arrays.toString(buffer)); + } + } + mListeningAddresses = Collections.unmodifiableSet(listeningAddresses); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mForwardingMode); + dest.writeInt(mMinScope); + dest.writeInt(mListeningAddresses.size()); + for (final Inet6Address addr : mListeningAddresses) { + dest.writeByteArray(addr.getAddress()); + } + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator<>() { + @Override + public MulticastRoutingConfig createFromParcel(Parcel in) { + return new MulticastRoutingConfig(in); + } + + @Override + public MulticastRoutingConfig[] newArray(int size) { + return new MulticastRoutingConfig[size]; + } + }; + + public static class Builder { + @MulticastForwardingMode + private final int mForwardingMode; + private int mMinScope; + private final ArraySet mListeningAddresses; + + private Builder(@MulticastForwardingMode final int mode, int scope) { + mForwardingMode = mode; + mMinScope = scope; + mListeningAddresses = new ArraySet<>(); + } + + /** + * Create a builder that forwards nothing. + * No properties can be set on such a builder. + */ + public static Builder newBuilderForwardingNone() { + return new Builder(FORWARD_NONE, MULTICAST_SCOPE_NONE); + } + + /** + * Create a builder that forwards packets above a certain scope + * + * The scope can be changed on this builder, but not the listening addresses. + * @param scope the initial scope + */ + public static Builder newBuilderWithMinScope(final int scope) { + return new Builder(FORWARD_WITH_MIN_SCOPE, scope); + } + + /** + * Create a builder that forwards a specified list of listening addresses. + * + * Addresses can be added and removed from this builder, but the scope can't be set. + */ + public static Builder newBuilderWithListeningAddresses() { + return new Builder(FORWARD_SELECTED, MULTICAST_SCOPE_NONE); + } + + /** + * Sets the minimum scope for this multicast routing config. + * This is only meaningful (indeed, allowed) for configs in FORWARD_WITH_MIN_SCOPE mode. + * @return this builder + */ + public Builder setMinimumScope(final int scope) { + if (FORWARD_WITH_MIN_SCOPE != mForwardingMode) { + throw new IllegalArgumentException("Can't set the scope on a builder in mode " + + modeToString(mForwardingMode)); + } + mMinScope = scope; + return this; + } + + /** + * Add an address to the set of listening addresses. + * + * This is only meaningful (indeed, allowed) for configs in FORWARD_SELECTED mode. + * If this address was already added, this is a no-op. + * @return this builder + */ + public Builder addListeningAddress(@NonNull final Inet6Address address) { + if (FORWARD_SELECTED != mForwardingMode) { + throw new IllegalArgumentException("Can't add an address on a builder in mode " + + modeToString(mForwardingMode)); + } + // TODO : should we check that this is a multicast address ? + mListeningAddresses.add(address); + return this; + } + + /** + * Remove an address from the set of listening addresses. + * + * This is only meaningful (indeed, allowed) for configs in FORWARD_SELECTED mode. + * If this address was not added, or was already removed, this is a no-op. + * @return this builder + */ + public Builder removeListeningAddress(@NonNull final Inet6Address address) { + if (FORWARD_SELECTED != mForwardingMode) { + throw new IllegalArgumentException("Can't remove an address on a builder in mode " + + modeToString(mForwardingMode)); + } + mListeningAddresses.remove(address); + return this; + } + + /** + * Build the config. + */ + public MulticastRoutingConfig build() { + return new MulticastRoutingConfig(mForwardingMode, mMinScope, mListeningAddresses); + } + } + + private static String modeToString(@MulticastForwardingMode final int mode) { + switch (mode) { + case FORWARD_NONE: return "FORWARD_NONE"; + case FORWARD_SELECTED: return "FORWARD_SELECTED"; + case FORWARD_WITH_MIN_SCOPE: return "FORWARD_WITH_MIN_SCOPE"; + default: return "unknown multicast routing mode " + mode; + } + } +} diff --git a/framework/src/android/net/NetworkAgent.java b/framework/src/android/net/NetworkAgent.java index 177f7e3d17..4e9087cbd3 100644 --- a/framework/src/android/net/NetworkAgent.java +++ b/framework/src/android/net/NetworkAgent.java @@ -151,7 +151,7 @@ public abstract class NetworkAgent { /** * Sent by the NetworkAgent to ConnectivityService to pass the current - * NetworkCapabilties. + * NetworkCapabilities. * obj = NetworkCapabilities * @hide */ @@ -442,6 +442,14 @@ public abstract class NetworkAgent { */ public static final int EVENT_UNREGISTER_AFTER_REPLACEMENT = BASE + 29; + /** + * Sent by the NetworkAgent to ConnectivityService to pass the new value of the local + * network agent config. + * obj = {@code Pair} + * @hide + */ + public static final int EVENT_LOCAL_NETWORK_CONFIG_CHANGED = BASE + 30; + /** * DSCP policy was successfully added. */ @@ -517,20 +525,47 @@ public abstract class NetworkAgent { @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, @NonNull NetworkScore score, @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) { - this(looper, context, logTag, nc, lp, score, config, + this(context, looper, logTag, nc, lp, null /* localNetworkConfig */, score, config, + provider); + } + + /** + * Create a new network agent. + * @param context a {@link Context} to get system services from. + * @param looper the {@link Looper} on which to invoke the callbacks. + * @param logTag the tag for logs + * @param nc the initial {@link NetworkCapabilities} of this network. Update with + * sendNetworkCapabilities. + * @param lp the initial {@link LinkProperties} of this network. Update with sendLinkProperties. + * @param localNetworkConfig the initial {@link LocalNetworkConfig} of this + * network. Update with sendLocalNetworkConfig. Must be + * non-null iff the nc have NET_CAPABILITY_LOCAL_NETWORK. + * @param score the initial score of this network. Update with sendNetworkScore. + * @param config an immutable {@link NetworkAgentConfig} for this agent. + * @param provider the {@link NetworkProvider} managing this agent. + * @hide + */ + // TODO : expose + public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag, + @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, + @Nullable LocalNetworkConfig localNetworkConfig, @NonNull NetworkScore score, + @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) { + this(looper, context, logTag, nc, lp, localNetworkConfig, score, config, provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(), getLegacyNetworkInfo(config)); } private static class InitialConfiguration { - public final Context context; - public final NetworkCapabilities capabilities; - public final LinkProperties properties; - public final NetworkScore score; - public final NetworkAgentConfig config; - public final NetworkInfo info; + @NonNull public final Context context; + @NonNull public final NetworkCapabilities capabilities; + @NonNull public final LinkProperties properties; + @NonNull public final NetworkScore score; + @NonNull public final NetworkAgentConfig config; + @NonNull public final NetworkInfo info; + @Nullable public final LocalNetworkConfig localNetworkConfig; InitialConfiguration(@NonNull Context context, @NonNull NetworkCapabilities capabilities, - @NonNull LinkProperties properties, @NonNull NetworkScore score, + @NonNull LinkProperties properties, + @Nullable LocalNetworkConfig localNetworkConfig, @NonNull NetworkScore score, @NonNull NetworkAgentConfig config, @NonNull NetworkInfo info) { this.context = context; this.capabilities = capabilities; @@ -538,14 +573,15 @@ public abstract class NetworkAgent { this.score = score; this.config = config; this.info = info; + this.localNetworkConfig = localNetworkConfig; } } private volatile InitialConfiguration mInitialConfiguration; private NetworkAgent(@NonNull Looper looper, @NonNull Context context, @NonNull String logTag, @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, - @NonNull NetworkScore score, @NonNull NetworkAgentConfig config, int providerId, - @NonNull NetworkInfo ni) { + @Nullable LocalNetworkConfig localNetworkConfig, @NonNull NetworkScore score, + @NonNull NetworkAgentConfig config, int providerId, @NonNull NetworkInfo ni) { mHandler = new NetworkAgentHandler(looper); LOG_TAG = logTag; mNetworkInfo = new NetworkInfo(ni); @@ -556,7 +592,7 @@ public abstract class NetworkAgent { mInitialConfiguration = new InitialConfiguration(context, new NetworkCapabilities(nc, NetworkCapabilities.REDACT_NONE), - new LinkProperties(lp), score, config, ni); + new LinkProperties(lp), localNetworkConfig, score, config, ni); } private class NetworkAgentHandler extends Handler { @@ -723,7 +759,8 @@ public abstract class NetworkAgent { mNetwork = cm.registerNetworkAgent(new NetworkAgentBinder(mHandler), new NetworkInfo(mInitialConfiguration.info), mInitialConfiguration.properties, mInitialConfiguration.capabilities, - mInitialConfiguration.score, mInitialConfiguration.config, providerId); + mInitialConfiguration.localNetworkConfig, mInitialConfiguration.score, + mInitialConfiguration.config, providerId); mInitialConfiguration = null; // All this memory can now be GC'd } return mNetwork; @@ -1098,6 +1135,18 @@ public abstract class NetworkAgent { queueOrSendMessage(reg -> reg.sendNetworkCapabilities(nc)); } + /** + * Must be called by the agent when the network's {@link LocalNetworkConfig} changes. + * @param config the new LocalNetworkConfig + * @hide + */ + public void sendLocalNetworkConfig(@NonNull LocalNetworkConfig config) { + Objects.requireNonNull(config); + // If the agent doesn't have NET_CAPABILITY_LOCAL_NETWORK, this will be ignored by + // ConnectivityService with a Log.wtf. + queueOrSendMessage(reg -> reg.sendLocalNetworkConfig(config)); + } + /** * Must be called by the agent to update the score of this network. * diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index ba0cad3425..a50404adb2 100755 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -161,6 +161,7 @@ import android.net.InetAddresses; import android.net.IpMemoryStore; import android.net.IpPrefix; import android.net.LinkProperties; +import android.net.LocalNetworkConfig; import android.net.MatchAllNetworkSpecifier; import android.net.NativeNetworkConfig; import android.net.NativeNetworkType; @@ -1775,7 +1776,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mNoServiceNetwork = new NetworkAgentInfo(null, new Network(INetd.UNREACHABLE_NET_ID), new NetworkInfo(TYPE_NONE, 0, "", ""), - new LinkProperties(), new NetworkCapabilities(), + new LinkProperties(), new NetworkCapabilities(), null /* localNetworkConfig */, new NetworkScore.Builder().setLegacyInt(0).build(), mContext, null, new NetworkAgentConfig(), this, null, null, 0, INVALID_UID, mLingerDelayMs, mQosCallbackTracker, mDeps); @@ -4123,6 +4124,11 @@ public class ConnectivityService extends IConnectivityManager.Stub updateNetworkInfo(nai, info); break; } + case NetworkAgent.EVENT_LOCAL_NETWORK_CONFIG_CHANGED: { + final LocalNetworkConfig config = (LocalNetworkConfig) arg.second; + updateLocalNetworkConfig(nai, config); + break; + } case NetworkAgent.EVENT_NETWORK_SCORE_CHANGED: { updateNetworkScore(nai, (NetworkScore) arg.second); break; @@ -8083,13 +8089,18 @@ public class ConnectivityService extends IConnectivityManager.Stub * @param networkCapabilities the initial capabilites of this network. They can be updated * later : see {@link #updateCapabilities}. * @param initialScore the initial score of the network. See {@link NetworkAgentInfo#getScore}. + * @param localNetworkConfig config about this local network, or null if not a local network * @param networkAgentConfig metadata about the network. This is never updated. * @param providerId the ID of the provider owning this NetworkAgent. * @return the network created for this agent. */ - public Network registerNetworkAgent(INetworkAgent na, NetworkInfo networkInfo, - LinkProperties linkProperties, NetworkCapabilities networkCapabilities, - @NonNull NetworkScore initialScore, NetworkAgentConfig networkAgentConfig, + public Network registerNetworkAgent(INetworkAgent na, + NetworkInfo networkInfo, + LinkProperties linkProperties, + NetworkCapabilities networkCapabilities, + @NonNull NetworkScore initialScore, + @Nullable LocalNetworkConfig localNetworkConfig, + NetworkAgentConfig networkAgentConfig, int providerId) { Objects.requireNonNull(networkInfo, "networkInfo must not be null"); Objects.requireNonNull(linkProperties, "linkProperties must not be null"); @@ -8107,12 +8118,20 @@ public class ConnectivityService extends IConnectivityManager.Stub // Before U, netd doesn't support PHYSICAL_LOCAL networks so this can't work. throw new IllegalArgumentException("Local agents are not supported in this version"); } + final boolean hasLocalNetworkConfig = null != localNetworkConfig; + if (hasLocalCap != hasLocalNetworkConfig) { + throw new IllegalArgumentException(null != localNetworkConfig + ? "Only local network agents can have a LocalNetworkConfig" + : "Local network agents must have a LocalNetworkConfig" + ); + } final int uid = mDeps.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { return registerNetworkAgentInternal(na, networkInfo, linkProperties, - networkCapabilities, initialScore, networkAgentConfig, providerId, uid); + networkCapabilities, initialScore, networkAgentConfig, localNetworkConfig, + providerId, uid); } finally { Binder.restoreCallingIdentity(token); } @@ -8120,7 +8139,8 @@ public class ConnectivityService extends IConnectivityManager.Stub private Network registerNetworkAgentInternal(INetworkAgent na, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, - NetworkScore currentScore, NetworkAgentConfig networkAgentConfig, int providerId, + NetworkScore currentScore, NetworkAgentConfig networkAgentConfig, + @Nullable LocalNetworkConfig localNetworkConfig, int providerId, int uid) { // Make a copy of the passed NI, LP, NC as the caller may hold a reference to them @@ -8128,6 +8148,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkInfo niCopy = new NetworkInfo(networkInfo); final NetworkCapabilities ncCopy = new NetworkCapabilities(networkCapabilities); final LinkProperties lpCopy = new LinkProperties(linkProperties); + // No need to copy |localNetworkConfiguration| as it is immutable. // At this point the capabilities/properties are untrusted and unverified, e.g. checks that // the capabilities' access UIDs comply with security limitations. They will be sanitized @@ -8135,9 +8156,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // because some of the checks must happen on the handler thread. final NetworkAgentInfo nai = new NetworkAgentInfo(na, new Network(mNetIdManager.reserveNetId()), niCopy, lpCopy, ncCopy, - currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig), - this, mNetd, mDnsResolver, providerId, uid, mLingerDelayMs, - mQosCallbackTracker, mDeps); + localNetworkConfig, currentScore, mContext, mTrackerHandler, + new NetworkAgentConfig(networkAgentConfig), this, mNetd, mDnsResolver, providerId, + uid, mLingerDelayMs, mQosCallbackTracker, mDeps); final String extraInfo = niCopy.getExtraInfo(); final String name = TextUtils.isEmpty(extraInfo) @@ -8872,6 +8893,16 @@ public class ConnectivityService extends IConnectivityManager.Stub updateCapabilities(nai.getScore(), nai, nai.networkCapabilities); } + private void updateLocalNetworkConfig(@NonNull final NetworkAgentInfo nai, + @NonNull final LocalNetworkConfig config) { + if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_LOCAL_NETWORK)) { + Log.wtf(TAG, "Ignoring update of a local network info on non-local network " + nai); + return; + } + // TODO : actually apply the diff. + nai.localNetworkConfig = config; + } + /** * Returns the interface which requires VPN isolation (ingress interface filtering). * diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java index 8d0d7116c9..b0ad978170 100644 --- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java +++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java @@ -35,6 +35,7 @@ import android.net.INetworkAgent; import android.net.INetworkAgentRegistry; import android.net.INetworkMonitor; import android.net.LinkProperties; +import android.net.LocalNetworkConfig; import android.net.NattKeepalivePacketData; import android.net.Network; import android.net.NetworkAgent; @@ -173,6 +174,7 @@ public class NetworkAgentInfo implements NetworkRanker.Scoreable { // TODO: make this private with a getter. @NonNull public NetworkCapabilities networkCapabilities; @NonNull public final NetworkAgentConfig networkAgentConfig; + @Nullable public LocalNetworkConfig localNetworkConfig; // Underlying networks declared by the agent. // The networks in this list might be declared by a VPN using setUnderlyingNetworks and are @@ -609,6 +611,7 @@ public class NetworkAgentInfo implements NetworkRanker.Scoreable { public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info, @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, + @Nullable LocalNetworkConfig localNetworkConfig, @NonNull NetworkScore score, Context context, Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd, IDnsResolver dnsResolver, int factorySerialNumber, int creatorUid, @@ -626,6 +629,7 @@ public class NetworkAgentInfo implements NetworkRanker.Scoreable { networkInfo = info; linkProperties = lp; networkCapabilities = nc; + this.localNetworkConfig = localNetworkConfig; networkAgentConfig = config; mConnService = connService; mConnServiceDeps = deps; @@ -904,6 +908,12 @@ public class NetworkAgentInfo implements NetworkRanker.Scoreable { new Pair<>(NetworkAgentInfo.this, info)).sendToTarget(); } + @Override + public void sendLocalNetworkConfig(@NonNull final LocalNetworkConfig config) { + mHandler.obtainMessage(NetworkAgent.EVENT_LOCAL_NETWORK_CONFIG_CHANGED, + new Pair<>(NetworkAgentInfo.this, config)).sendToTarget(); + } + @Override public void sendScore(@NonNull final NetworkScore score) { mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_SCORE_CHANGED, diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt index 5937655925..392cba9bb8 100644 --- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt +++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt @@ -704,6 +704,7 @@ class NetworkAgentTest { argThat { it.detailedState == NetworkInfo.DetailedState.CONNECTING }, any(LinkProperties::class.java), any(NetworkCapabilities::class.java), + any(), // LocalNetworkConfig TODO : specify when it's public any(NetworkScore::class.java), any(NetworkAgentConfig::class.java), eq(NetworkProvider.ID_NONE)) diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java index 16f0c44b50..04fc9ef43a 100755 --- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java @@ -12747,7 +12747,8 @@ public class ConnectivityServiceTest { private NetworkAgentInfo fakeNai(NetworkCapabilities nc, NetworkInfo networkInfo) { return new NetworkAgentInfo(null, new Network(NET_ID), networkInfo, new LinkProperties(), - nc, new NetworkScore.Builder().setLegacyInt(0).build(), + nc, null /* localNetworkConfig */, + new NetworkScore.Builder().setLegacyInt(0).build(), mServiceContext, null, new NetworkAgentConfig(), mService, null, null, 0, INVALID_UID, TEST_LINGER_DELAY_MS, mQosCallbackTracker, new ConnectivityService.Dependencies()); diff --git a/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java index e6c0c8348d..07883ff8f8 100644 --- a/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java +++ b/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java @@ -372,9 +372,10 @@ public class LingerMonitorTest { caps.addCapability(0); caps.addTransportType(transport); NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info, - new LinkProperties(), caps, new NetworkScore.Builder().setLegacyInt(50).build(), - mCtx, null, new NetworkAgentConfig.Builder().build(), mConnService, mNetd, - mDnsResolver, NetworkProvider.ID_NONE, Binder.getCallingUid(), TEST_LINGER_DELAY_MS, + new LinkProperties(), caps, null /* localNetworkConfiguration */, + new NetworkScore.Builder().setLegacyInt(50).build(), mCtx, null, + new NetworkAgentConfig.Builder().build(), mConnService, mNetd, mDnsResolver, + NetworkProvider.ID_NONE, Binder.getCallingUid(), TEST_LINGER_DELAY_MS, mQosCallbackTracker, new ConnectivityService.Dependencies()); if (setEverValidated) { // As tests in this class deal with testing lingering, most tests are interested diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java index d67476787a..478ff61c0a 100644 --- a/tests/unit/java/com/android/server/connectivity/VpnTest.java +++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java @@ -3004,8 +3004,15 @@ public class VpnTest extends VpnTestBase { profile.mppe = useMppe; doReturn(new Network[] { new Network(101) }).when(mConnectivityManager).getAllNetworks(); - doReturn(new Network(102)).when(mConnectivityManager).registerNetworkAgent(any(), any(), - any(), any(), any(), any(), anyInt()); + doReturn(new Network(102)).when(mConnectivityManager).registerNetworkAgent( + any(), // INetworkAgent + any(), // NetworkInfo + any(), // LinkProperties + any(), // NetworkCapabilities + any(), // LocalNetworkConfig + any(), // NetworkScore + any(), // NetworkAgentConfig + anyInt()); // provider ID final Vpn vpn = startLegacyVpn(createVpn(PRIMARY_USER.id), profile); final TestDeps deps = (TestDeps) vpn.mDeps; @@ -3027,8 +3034,15 @@ public class VpnTest extends VpnTestBase { assertEquals("nomppe", mtpdArgs[argsPrefix.length]); } - verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(), - any(), any(), any(), any(), anyInt()); + verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent( + any(), // INetworkAgent + any(), // NetworkInfo + any(), // LinkProperties + any(), // NetworkCapabilities + any(), // LocalNetworkConfig + any(), // NetworkScore + any(), // NetworkAgentConfig + anyInt()); // provider ID }, () -> { // Cleanup vpn.mVpnRunner.exitVpnRunner(); deps.getStateFile().delete(); // set to delete on exit, but this deletes it earlier @@ -3053,7 +3067,7 @@ public class VpnTest extends VpnTestBase { .thenReturn(new Network[] { new Network(101) }); when(mConnectivityManager.registerNetworkAgent(any(), any(), any(), any(), - any(), any(), anyInt())).thenAnswer(invocation -> { + any(), any(), any(), anyInt())).thenAnswer(invocation -> { // The runner has registered an agent and is now ready. legacyRunnerReady.open(); return new Network(102); @@ -3079,7 +3093,7 @@ public class VpnTest extends VpnTestBase { ArgumentCaptor ncCaptor = ArgumentCaptor.forClass(NetworkCapabilities.class); verify(mConnectivityManager, timeout(10_000)).registerNetworkAgent(any(), any(), - lpCaptor.capture(), ncCaptor.capture(), any(), any(), anyInt()); + lpCaptor.capture(), ncCaptor.capture(), any(), any(), any(), anyInt()); // In this test the expected address is always v4 so /32. // Note that the interface needs to be specified because RouteInfo objects stored in diff --git a/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt index 86426c2066..6220e76409 100644 --- a/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt +++ b/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt @@ -16,6 +16,7 @@ package com.android.server +import android.net.LocalNetworkConfig import android.net.NetworkCapabilities import android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK import android.net.NetworkCapabilities.TRANSPORT_WIFI @@ -45,8 +46,9 @@ class CSKeepConnectedTest : CSTest() { .build() val keepConnectedAgent = Agent(nc = nc, score = FromS(NetworkScore.Builder() .setKeepConnectedReason(KEEP_CONNECTED_DOWNSTREAM_NETWORK) - .build())) - val dontKeepConnectedAgent = Agent(nc = nc) + .build()), + lnc = LocalNetworkConfig.Builder().build()) + val dontKeepConnectedAgent = Agent(nc = nc, lnc = LocalNetworkConfig.Builder().build()) doTestKeepConnected(keepConnectedAgent, dontKeepConnectedAgent) } diff --git a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt index 7914e04958..cfc3a3d8e0 100644 --- a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt +++ b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt @@ -18,6 +18,7 @@ package com.android.server import android.content.pm.PackageManager.FEATURE_LEANBACK import android.net.INetd +import android.net.LocalNetworkConfig import android.net.NativeNetworkConfig import android.net.NativeNetworkType import android.net.NetworkCapabilities @@ -48,6 +49,8 @@ private const val NO_CALLBACK_TIMEOUT_MS = 200L private fun keepConnectedScore() = FromS(NetworkScore.Builder().setKeepConnectedReason(KEEP_CONNECTED_FOR_TEST).build()) +private fun defaultLnc() = LocalNetworkConfig.Builder().build() + @RunWith(DevSdkIgnoreRunner::class) @SmallTest @IgnoreUpTo(Build.VERSION_CODES.R) @@ -93,9 +96,9 @@ class CSLocalAgentCreationTests( addCapability(NET_CAPABILITY_LOCAL_NETWORK) }.build() val localAgent = if (sdkLevel >= VERSION_V || sdkLevel == VERSION_U && isTv) { - Agent(nc = ncTemplate, score = keepConnectedScore()) + Agent(nc = ncTemplate, score = keepConnectedScore(), lnc = defaultLnc()) } else { - assertFailsWith { Agent(nc = ncTemplate) } + assertFailsWith { Agent(nc = ncTemplate, lnc = defaultLnc()) } netdInOrder.verify(netd, never()).networkCreate(any()) return } @@ -111,4 +114,18 @@ class CSLocalAgentCreationTests( localAgent.disconnect() netdInOrder.verify(netd, timeout(TIMEOUT_MS)).networkDestroy(localAgent.network.netId) } + + @Test + fun testBadAgents() { + assertFailsWith { + Agent(nc = NetworkCapabilities.Builder() + .addCapability(NET_CAPABILITY_LOCAL_NETWORK) + .build(), + lnc = null) + } + assertFailsWith { + Agent(nc = NetworkCapabilities.Builder().build(), + lnc = LocalNetworkConfig.Builder().build()) + } + } } diff --git a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt new file mode 100644 index 0000000000..bd3efa91ba --- /dev/null +++ b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt @@ -0,0 +1,111 @@ +/* + * 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.IpPrefix +import android.net.LinkAddress +import android.net.LinkProperties +import android.net.LocalNetworkConfig +import android.net.NetworkCapabilities +import android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK +import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED +import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING +import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED +import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED +import android.net.NetworkCapabilities.TRANSPORT_WIFI +import android.net.NetworkRequest +import android.net.RouteInfo +import android.os.Build +import com.android.testutils.DevSdkIgnoreRule +import com.android.testutils.DevSdkIgnoreRunner +import com.android.testutils.RecorderCallback +import com.android.testutils.RecorderCallback.CallbackEntry.Available +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.TestableNetworkCallback +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.test.assertFailsWith + +private fun nc(transport: Int, vararg caps: Int) = NetworkCapabilities.Builder().apply { + addTransportType(transport) + caps.forEach { + addCapability(it) + } + // Useful capabilities for everybody + addCapability(NET_CAPABILITY_NOT_RESTRICTED) + addCapability(NET_CAPABILITY_NOT_SUSPENDED) + addCapability(NET_CAPABILITY_NOT_ROAMING) + addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) +}.build() + +private fun lp(iface: String) = LinkProperties().apply { + interfaceName = iface + addLinkAddress(LinkAddress(LOCAL_IPV4_ADDRESS, 32)) + addRoute(RouteInfo(IpPrefix("0.0.0.0/0"), null, null)) +} + +@RunWith(DevSdkIgnoreRunner::class) +@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) +class CSLocalAgentTests : CSTest() { + @Test + fun testBadAgents() { + assertFailsWith { + Agent(nc = NetworkCapabilities.Builder() + .addCapability(NET_CAPABILITY_LOCAL_NETWORK) + .build(), + lnc = null) + } + assertFailsWith { + Agent(nc = NetworkCapabilities.Builder().build(), + lnc = LocalNetworkConfig.Builder().build()) + } + } + + @Test + fun testUpdateLocalAgentConfig() { + deps.setBuildSdk(VERSION_V) + + val cb = TestableNetworkCallback() + cm.requestNetwork(NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_LOCAL_NETWORK) + .build(), + cb) + + // Set up a local agent that should forward its traffic to the best DUN upstream. + val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK), + lp = lp("local0"), + lnc = LocalNetworkConfig.Builder().build(), + ) + localAgent.connect() + + cb.expect(localAgent.network) + cb.expect(localAgent.network) + cb.expect(localAgent.network) + cb.expect(localAgent.network) + + val newLnc = LocalNetworkConfig.Builder() + .setUpstreamSelector(NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI) + .build()) + .build() + localAgent.sendLocalNetworkConfig(newLnc) + + localAgent.disconnect() + } +} diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt index 1d0d5df3fd..094ded3ae0 100644 --- a/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt +++ b/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt @@ -21,6 +21,7 @@ import android.net.ConnectivityManager import android.net.INetworkMonitor import android.net.INetworkMonitorCallbacks import android.net.LinkProperties +import android.net.LocalNetworkConfig import android.net.Network import android.net.NetworkAgent import android.net.NetworkAgentConfig @@ -69,6 +70,7 @@ class CSAgentWrapper( nac: NetworkAgentConfig, val nc: NetworkCapabilities, val lp: LinkProperties, + val lnc: LocalNetworkConfig?, val score: FromS, val provider: NetworkProvider? ) : TestableNetworkCallback.HasNetwork { @@ -100,7 +102,7 @@ class CSAgentWrapper( // Create the actual agent. NetworkAgent is abstract, so make an anonymous subclass. if (deps.isAtLeastS()) { agent = object : NetworkAgent(context, csHandlerThread.looper, TAG, - nc, lp, score.value, nac, provider) {} + nc, lp, lnc, score.value, nac, provider) {} } else { agent = object : NetworkAgent(context, csHandlerThread.looper, TAG, nc, lp, 50 /* score */, nac, provider) {} @@ -165,4 +167,6 @@ class CSAgentWrapper( agent.unregister() cb.eventuallyExpect { it.network == agent.network } } + + fun sendLocalNetworkConfig(lnc: LocalNetworkConfig) = agent.sendLocalNetworkConfig(lnc) } diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt index 2f78212768..0ccbfc3e80 100644 --- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt +++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt @@ -27,6 +27,7 @@ import android.net.ConnectivityManager import android.net.INetd import android.net.InetAddresses import android.net.LinkProperties +import android.net.LocalNetworkConfig import android.net.NetworkAgentConfig import android.net.NetworkCapabilities import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED @@ -292,10 +293,11 @@ open class CSTest { nc: NetworkCapabilities = defaultNc(), nac: NetworkAgentConfig = emptyAgentConfig(nc.getLegacyType()), lp: LinkProperties = defaultLp(), + lnc: LocalNetworkConfig? = null, score: FromS = defaultScore(), provider: NetworkProvider? = null - ) = CSAgentWrapper(context, deps, csHandlerThread, networkStack, nac, nc, lp, score, provider) - + ) = CSAgentWrapper(context, deps, csHandlerThread, networkStack, + nac, nc, lp, lnc, score, provider) fun Agent(vararg transports: Int, lp: LinkProperties = defaultLp()): CSAgentWrapper { val nc = NetworkCapabilities.Builder().apply { transports.forEach {