Merge changes from topic "tetherlocalagent" into main
* changes: Add integration test of tethering local network agent feature Add unit test of tethering local network agent feature Support LocalNetworkAgent for Tethering
This commit is contained in:
commit
0b7d710ba0
8 changed files with 565 additions and 53 deletions
|
|
@ -31,6 +31,7 @@ import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
|
import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
|
import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
||||||
|
import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
|
import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
|
import static android.net.TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
|
||||||
import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration;
|
import static android.net.TetheringManager.TetheringRequest.checkStaticAddressConfiguration;
|
||||||
|
|
@ -40,19 +41,25 @@ import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
|
||||||
|
|
||||||
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
|
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
|
||||||
import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
|
import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
|
||||||
|
import static com.android.networkstack.tethering.TetheringConfiguration.TETHERING_LOCAL_NETWORK_AGENT;
|
||||||
import static com.android.networkstack.tethering.TetheringConfiguration.USE_SYNC_SM;
|
import static com.android.networkstack.tethering.TetheringConfiguration.USE_SYNC_SM;
|
||||||
import static com.android.networkstack.tethering.util.PrefixUtils.asIpPrefix;
|
import static com.android.networkstack.tethering.util.PrefixUtils.asIpPrefix;
|
||||||
import static com.android.networkstack.tethering.util.TetheringMessageBase.BASE_IPSERVER;
|
import static com.android.networkstack.tethering.util.TetheringMessageBase.BASE_IPSERVER;
|
||||||
|
import static com.android.networkstack.tethering.util.TetheringUtils.getTransportTypeForTetherableType;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
import android.net.INetd;
|
import android.net.INetd;
|
||||||
import android.net.INetworkStackStatusCallback;
|
import android.net.INetworkStackStatusCallback;
|
||||||
import android.net.IpPrefix;
|
import android.net.IpPrefix;
|
||||||
import android.net.LinkAddress;
|
import android.net.LinkAddress;
|
||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.MacAddress;
|
import android.net.MacAddress;
|
||||||
|
import android.net.NetworkAgent;
|
||||||
import android.net.RouteInfo;
|
import android.net.RouteInfo;
|
||||||
import android.net.TetheredClient;
|
import android.net.TetheredClient;
|
||||||
import android.net.TetheringManager.TetheringRequest;
|
import android.net.TetheringManager.TetheringRequest;
|
||||||
|
import android.net.connectivity.ConnectivityInternalApiUtil;
|
||||||
import android.net.dhcp.DhcpLeaseParcelable;
|
import android.net.dhcp.DhcpLeaseParcelable;
|
||||||
import android.net.dhcp.DhcpServerCallbacks;
|
import android.net.dhcp.DhcpServerCallbacks;
|
||||||
import android.net.dhcp.DhcpServingParamsParcel;
|
import android.net.dhcp.DhcpServingParamsParcel;
|
||||||
|
|
@ -60,7 +67,9 @@ import android.net.dhcp.DhcpServingParamsParcelExt;
|
||||||
import android.net.dhcp.IDhcpEventCallbacks;
|
import android.net.dhcp.IDhcpEventCallbacks;
|
||||||
import android.net.dhcp.IDhcpServer;
|
import android.net.dhcp.IDhcpServer;
|
||||||
import android.net.ip.RouterAdvertisementDaemon.RaParams;
|
import android.net.ip.RouterAdvertisementDaemon.RaParams;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceSpecificException;
|
import android.os.ServiceSpecificException;
|
||||||
|
|
@ -70,11 +79,13 @@ import android.util.SparseArray;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.MessageUtils;
|
import com.android.internal.util.MessageUtils;
|
||||||
import com.android.internal.util.State;
|
import com.android.internal.util.State;
|
||||||
import com.android.modules.utils.build.SdkLevel;
|
import com.android.modules.utils.build.SdkLevel;
|
||||||
|
import com.android.net.module.util.DeviceConfigUtils;
|
||||||
import com.android.net.module.util.IIpv4PrefixRequest;
|
import com.android.net.module.util.IIpv4PrefixRequest;
|
||||||
import com.android.net.module.util.InterfaceParams;
|
import com.android.net.module.util.InterfaceParams;
|
||||||
import com.android.net.module.util.NetdUtils;
|
import com.android.net.module.util.NetdUtils;
|
||||||
|
|
@ -204,6 +215,23 @@ public class IpServer extends StateMachineShim {
|
||||||
/** Create a DhcpServer instance to be used by IpServer. */
|
/** Create a DhcpServer instance to be used by IpServer. */
|
||||||
public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
|
public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
|
||||||
DhcpServerCallbacks cb);
|
DhcpServerCallbacks cb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see DeviceConfigUtils#isTetheringFeatureEnabled
|
||||||
|
*/
|
||||||
|
public boolean isFeatureEnabled(Context context, String name) {
|
||||||
|
return DeviceConfigUtils.isTetheringFeatureEnabled(context, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create a NetworkAgent instance to be used by IpServer. */
|
||||||
|
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
public NetworkAgent makeNetworkAgent(
|
||||||
|
@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
|
||||||
|
int interfaceType, @NonNull LinkProperties lp) {
|
||||||
|
return ConnectivityInternalApiUtil.buildTetheringNetworkAgent(
|
||||||
|
context, looper, logTag, getTransportTypeForTetherableType(interfaceType), lp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// request from the user that it wants to tether
|
// request from the user that it wants to tether
|
||||||
|
|
@ -304,16 +332,28 @@ public class IpServer extends StateMachineShim {
|
||||||
|
|
||||||
private final TetheringMetrics mTetheringMetrics;
|
private final TetheringMetrics mTetheringMetrics;
|
||||||
private final Handler mHandler;
|
private final Handler mHandler;
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
|
private final boolean mSupportLocalAgent;
|
||||||
|
|
||||||
|
// This will be null if the TetheredState is not entered or feature not supported.
|
||||||
|
// This will be only accessed from the IpServer handler thread.
|
||||||
|
private NetworkAgent mTetheringAgent;
|
||||||
|
|
||||||
|
private static boolean everRegistered(@NonNull NetworkAgent agent) {
|
||||||
|
return agent.getNetwork() != null;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Add a dependency object to pass the data members or variables from the tethering
|
// TODO: Add a dependency object to pass the data members or variables from the tethering
|
||||||
// object. It helps to reduce the arguments of the constructor.
|
// object. It helps to reduce the arguments of the constructor.
|
||||||
public IpServer(
|
public IpServer(
|
||||||
String ifaceName, Handler handler, int interfaceType, SharedLog log,
|
String ifaceName, @NonNull Context context, Handler handler, int interfaceType,
|
||||||
INetd netd, @NonNull BpfCoordinator bpfCoordinator,
|
SharedLog log, INetd netd, @NonNull BpfCoordinator bpfCoordinator,
|
||||||
RoutingCoordinatorManager routingCoordinatorManager, Callback callback,
|
RoutingCoordinatorManager routingCoordinatorManager, Callback callback,
|
||||||
TetheringConfiguration config,
|
TetheringConfiguration config,
|
||||||
TetheringMetrics tetheringMetrics, Dependencies deps) {
|
TetheringMetrics tetheringMetrics, Dependencies deps) {
|
||||||
super(ifaceName, USE_SYNC_SM ? null : handler.getLooper());
|
super(ifaceName, USE_SYNC_SM ? null : handler.getLooper());
|
||||||
|
mContext = Objects.requireNonNull(context);
|
||||||
mHandler = handler;
|
mHandler = handler;
|
||||||
mLog = log.forSubComponent(ifaceName);
|
mLog = log.forSubComponent(ifaceName);
|
||||||
mNetd = netd;
|
mNetd = netd;
|
||||||
|
|
@ -339,6 +379,10 @@ public class IpServer extends StateMachineShim {
|
||||||
mLastError = TETHER_ERROR_NO_ERROR;
|
mLastError = TETHER_ERROR_NO_ERROR;
|
||||||
mServingMode = STATE_AVAILABLE;
|
mServingMode = STATE_AVAILABLE;
|
||||||
|
|
||||||
|
// Tethering network agent is supported on V+, and will be rolled out gradually.
|
||||||
|
mSupportLocalAgent = SdkLevel.isAtLeastV()
|
||||||
|
&& mDeps.isFeatureEnabled(mContext, TETHERING_LOCAL_NETWORK_AGENT);
|
||||||
|
|
||||||
mInitialState = new InitialState();
|
mInitialState = new InitialState();
|
||||||
mLocalHotspotState = new LocalHotspotState();
|
mLocalHotspotState = new LocalHotspotState();
|
||||||
mTetheredState = new TetheredState();
|
mTetheredState = new TetheredState();
|
||||||
|
|
@ -787,6 +831,7 @@ public class IpServer extends StateMachineShim {
|
||||||
//
|
//
|
||||||
// TODO: Evaluate using a data structure than is more directly suited to
|
// TODO: Evaluate using a data structure than is more directly suited to
|
||||||
// communicating only the relevant information.
|
// communicating only the relevant information.
|
||||||
|
@SuppressLint("NewApi")
|
||||||
private void updateUpstreamIPv6LinkProperties(LinkProperties v6only, int ttlAdjustment) {
|
private void updateUpstreamIPv6LinkProperties(LinkProperties v6only, int ttlAdjustment) {
|
||||||
if (mRaDaemon == null) return;
|
if (mRaDaemon == null) return;
|
||||||
|
|
||||||
|
|
@ -847,8 +892,7 @@ public class IpServer extends StateMachineShim {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeRoutesFromNetwork(int netId, @NonNull final List<RouteInfo> toBeRemoved) {
|
private void removeRoutesFromNetwork(int netId, @NonNull final List<RouteInfo> toBeRemoved) {
|
||||||
final int removalFailures = NetdUtils.removeRoutesFromNetwork(
|
final int removalFailures = NetdUtils.removeRoutesFromNetwork(mNetd, netId, toBeRemoved);
|
||||||
mNetd, netId, toBeRemoved);
|
|
||||||
if (removalFailures > 0) {
|
if (removalFailures > 0) {
|
||||||
mLog.e("Failed to remove " + removalFailures
|
mLog.e("Failed to remove " + removalFailures
|
||||||
+ " IPv6 routes from network " + netId + ".");
|
+ " IPv6 routes from network " + netId + ".");
|
||||||
|
|
@ -900,7 +944,9 @@ public class IpServer extends StateMachineShim {
|
||||||
if (!deprecatedPrefixes.isEmpty()) {
|
if (!deprecatedPrefixes.isEmpty()) {
|
||||||
final List<RouteInfo> routesToBeRemoved =
|
final List<RouteInfo> routesToBeRemoved =
|
||||||
getLocalRoutesFor(mIfaceName, deprecatedPrefixes);
|
getLocalRoutesFor(mIfaceName, deprecatedPrefixes);
|
||||||
removeRoutesFromNetwork(LOCAL_NET_ID, routesToBeRemoved);
|
if (mTetheringAgent == null) {
|
||||||
|
removeRoutesFromNetwork(LOCAL_NET_ID, routesToBeRemoved);
|
||||||
|
}
|
||||||
for (RouteInfo route : routesToBeRemoved) mLinkProperties.removeRoute(route);
|
for (RouteInfo route : routesToBeRemoved) mLinkProperties.removeRoute(route);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -914,7 +960,9 @@ public class IpServer extends StateMachineShim {
|
||||||
if (!addedPrefixes.isEmpty()) {
|
if (!addedPrefixes.isEmpty()) {
|
||||||
final List<RouteInfo> routesToBeAdded =
|
final List<RouteInfo> routesToBeAdded =
|
||||||
getLocalRoutesFor(mIfaceName, addedPrefixes);
|
getLocalRoutesFor(mIfaceName, addedPrefixes);
|
||||||
addRoutesToNetwork(LOCAL_NET_ID, routesToBeAdded);
|
if (mTetheringAgent == null) {
|
||||||
|
addRoutesToNetwork(LOCAL_NET_ID, routesToBeAdded);
|
||||||
|
}
|
||||||
for (RouteInfo route : routesToBeAdded) mLinkProperties.addRoute(route);
|
for (RouteInfo route : routesToBeAdded) mLinkProperties.addRoute(route);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1112,7 +1160,26 @@ public class IpServer extends StateMachineShim {
|
||||||
return CONNECTIVITY_SCOPE_LOCAL;
|
return CONNECTIVITY_SCOPE_LOCAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
private void startServingInterface() {
|
private void startServingInterface() {
|
||||||
|
// TODO: Enable Network Agent for Wifi P2P Group Owner mode when Network Agent
|
||||||
|
// for Group Client mode is supported.
|
||||||
|
if (mSupportLocalAgent && getScope() == CONNECTIVITY_SCOPE_GLOBAL) {
|
||||||
|
try {
|
||||||
|
mTetheringAgent = mDeps.makeNetworkAgent(mContext, Looper.myLooper(), TAG,
|
||||||
|
mInterfaceType, mLinkProperties);
|
||||||
|
// Entering CONNECTING state, the ConnectivityService will create the
|
||||||
|
// native network.
|
||||||
|
mTetheringAgent.register();
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
mLog.e("Error Creating Local Network", e);
|
||||||
|
// If an exception occurs during the creation or registration of the
|
||||||
|
// NetworkAgent, it typically indicates a problem with the system services.
|
||||||
|
mLastError = TETHER_ERROR_SERVICE_UNAVAIL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!startIPv4(getScope())) {
|
if (!startIPv4(getScope())) {
|
||||||
mLastError = TETHER_ERROR_IFACE_CFG_ERROR;
|
mLastError = TETHER_ERROR_IFACE_CFG_ERROR;
|
||||||
return;
|
return;
|
||||||
|
|
@ -1122,14 +1189,16 @@ public class IpServer extends StateMachineShim {
|
||||||
// Enable IPv6, disable accepting RA, etc. See TetherController::tetherInterface()
|
// Enable IPv6, disable accepting RA, etc. See TetherController::tetherInterface()
|
||||||
// for more detail.
|
// for more detail.
|
||||||
mNetd.tetherInterfaceAdd(mIfaceName);
|
mNetd.tetherInterfaceAdd(mIfaceName);
|
||||||
NetdUtils.networkAddInterface(mNetd, LOCAL_NET_ID, mIfaceName,
|
if (mTetheringAgent == null) {
|
||||||
20 /* maxAttempts */, 50 /* pollingIntervalMs */);
|
NetdUtils.networkAddInterface(mNetd, LOCAL_NET_ID, mIfaceName,
|
||||||
// Activate a route to dest and IPv6 link local.
|
20 /* maxAttempts */, 50 /* pollingIntervalMs */);
|
||||||
NetdUtils.modifyRoute(mNetd, NetdUtils.ModifyOperation.ADD, LOCAL_NET_ID,
|
// Activate a route to dest and IPv6 link local.
|
||||||
new RouteInfo(asIpPrefix(mIpv4Address), null, mIfaceName, RTN_UNICAST));
|
NetdUtils.modifyRoute(mNetd, NetdUtils.ModifyOperation.ADD, LOCAL_NET_ID,
|
||||||
NetdUtils.modifyRoute(mNetd, NetdUtils.ModifyOperation.ADD, LOCAL_NET_ID,
|
new RouteInfo(asIpPrefix(mIpv4Address), null, mIfaceName, RTN_UNICAST));
|
||||||
new RouteInfo(new IpPrefix("fe80::/64"), null, mIfaceName,
|
NetdUtils.modifyRoute(mNetd, NetdUtils.ModifyOperation.ADD, LOCAL_NET_ID,
|
||||||
RTN_UNICAST));
|
new RouteInfo(new IpPrefix("fe80::/64"), null, mIfaceName,
|
||||||
|
RTN_UNICAST));
|
||||||
|
}
|
||||||
} catch (RemoteException | ServiceSpecificException | IllegalStateException e) {
|
} catch (RemoteException | ServiceSpecificException | IllegalStateException e) {
|
||||||
mLog.e("Error Tethering", e);
|
mLog.e("Error Tethering", e);
|
||||||
mLastError = TETHER_ERROR_TETHER_IFACE_ERROR;
|
mLastError = TETHER_ERROR_TETHER_IFACE_ERROR;
|
||||||
|
|
@ -1141,9 +1210,17 @@ public class IpServer extends StateMachineShim {
|
||||||
// TODO: Make this a fatal error once Bluetooth IPv6 is sorted.
|
// TODO: Make this a fatal error once Bluetooth IPv6 is sorted.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mTetheringAgent != null && everRegistered(mTetheringAgent)) {
|
||||||
|
mTetheringAgent.sendLinkProperties(mLinkProperties);
|
||||||
|
// Mark it connected to notify the applications for
|
||||||
|
// the network availability.
|
||||||
|
mTetheringAgent.markConnected();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressLint("NewApi")
|
||||||
public void exit() {
|
public void exit() {
|
||||||
// Note that at this point, we're leaving the tethered state. We can fail any
|
// Note that at this point, we're leaving the tethered state. We can fail any
|
||||||
// of these operations, but it doesn't really change that we have to try them
|
// of these operations, but it doesn't really change that we have to try them
|
||||||
|
|
@ -1155,7 +1232,9 @@ public class IpServer extends StateMachineShim {
|
||||||
try {
|
try {
|
||||||
mNetd.tetherInterfaceRemove(mIfaceName);
|
mNetd.tetherInterfaceRemove(mIfaceName);
|
||||||
} finally {
|
} finally {
|
||||||
mNetd.networkRemoveInterface(LOCAL_NET_ID, mIfaceName);
|
if (mTetheringAgent == null) {
|
||||||
|
mNetd.networkRemoveInterface(LOCAL_NET_ID, mIfaceName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (RemoteException | ServiceSpecificException e) {
|
} catch (RemoteException | ServiceSpecificException e) {
|
||||||
mLastError = TETHER_ERROR_UNTETHER_IFACE_ERROR;
|
mLastError = TETHER_ERROR_UNTETHER_IFACE_ERROR;
|
||||||
|
|
@ -1165,6 +1244,11 @@ public class IpServer extends StateMachineShim {
|
||||||
stopIPv4();
|
stopIPv4();
|
||||||
mBpfCoordinator.removeIpServer(IpServer.this);
|
mBpfCoordinator.removeIpServer(IpServer.this);
|
||||||
|
|
||||||
|
if (mTetheringAgent != null && everRegistered(mTetheringAgent)) {
|
||||||
|
mTetheringAgent.unregister();
|
||||||
|
mTetheringAgent = null;
|
||||||
|
}
|
||||||
|
|
||||||
resetLinkProperties();
|
resetLinkProperties();
|
||||||
|
|
||||||
mTetheringMetrics.updateErrorCode(mInterfaceType, mLastError);
|
mTetheringMetrics.updateErrorCode(mInterfaceType, mLastError);
|
||||||
|
|
@ -1184,6 +1268,12 @@ public class IpServer extends StateMachineShim {
|
||||||
break;
|
break;
|
||||||
case CMD_IPV6_TETHER_UPDATE:
|
case CMD_IPV6_TETHER_UPDATE:
|
||||||
updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);
|
updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);
|
||||||
|
// Sends update to the NetworkAgent.
|
||||||
|
// TODO: Refactor the callers of sendLinkProperties()
|
||||||
|
// and move these code into sendLinkProperties().
|
||||||
|
if (mTetheringAgent != null && everRegistered(mTetheringAgent)) {
|
||||||
|
mTetheringAgent.sendLinkProperties(mLinkProperties);
|
||||||
|
}
|
||||||
sendLinkProperties();
|
sendLinkProperties();
|
||||||
break;
|
break;
|
||||||
case CMD_IP_FORWARDING_ENABLE_ERROR:
|
case CMD_IP_FORWARDING_ENABLE_ERROR:
|
||||||
|
|
@ -1236,13 +1326,17 @@ public class IpServer extends StateMachineShim {
|
||||||
// Remove deprecated routes from downstream network.
|
// Remove deprecated routes from downstream network.
|
||||||
final List<RouteInfo> routesToBeRemoved =
|
final List<RouteInfo> routesToBeRemoved =
|
||||||
List.of(getDirectConnectedRoute(deprecatedLinkAddress));
|
List.of(getDirectConnectedRoute(deprecatedLinkAddress));
|
||||||
removeRoutesFromNetwork(LOCAL_NET_ID, routesToBeRemoved);
|
if (mTetheringAgent == null) {
|
||||||
|
removeRoutesFromNetwork(LOCAL_NET_ID, routesToBeRemoved);
|
||||||
|
}
|
||||||
for (RouteInfo route : routesToBeRemoved) mLinkProperties.removeRoute(route);
|
for (RouteInfo route : routesToBeRemoved) mLinkProperties.removeRoute(route);
|
||||||
mLinkProperties.removeLinkAddress(deprecatedLinkAddress);
|
mLinkProperties.removeLinkAddress(deprecatedLinkAddress);
|
||||||
|
|
||||||
// Add new routes to downstream network.
|
// Add new routes to downstream network.
|
||||||
final List<RouteInfo> routesToBeAdded = List.of(getDirectConnectedRoute(mIpv4Address));
|
final List<RouteInfo> routesToBeAdded = List.of(getDirectConnectedRoute(mIpv4Address));
|
||||||
addRoutesToNetwork(LOCAL_NET_ID, routesToBeAdded);
|
if (mTetheringAgent == null) {
|
||||||
|
addRoutesToNetwork(LOCAL_NET_ID, routesToBeAdded);
|
||||||
|
}
|
||||||
for (RouteInfo route : routesToBeAdded) mLinkProperties.addRoute(route);
|
for (RouteInfo route : routesToBeAdded) mLinkProperties.addRoute(route);
|
||||||
mLinkProperties.addLinkAddress(mIpv4Address);
|
mLinkProperties.addLinkAddress(mIpv4Address);
|
||||||
|
|
||||||
|
|
@ -1255,6 +1349,10 @@ public class IpServer extends StateMachineShim {
|
||||||
mLog.e("Failed to update local DNS caching server");
|
mLog.e("Failed to update local DNS caching server");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Sends update to the NetworkAgent.
|
||||||
|
if (mTetheringAgent != null && everRegistered(mTetheringAgent)) {
|
||||||
|
mTetheringAgent.sendLinkProperties(mLinkProperties);
|
||||||
|
}
|
||||||
sendLinkProperties();
|
sendLinkProperties();
|
||||||
|
|
||||||
// Notify DHCP server that new prefix/route has been applied on IpServer.
|
// Notify DHCP server that new prefix/route has been applied on IpServer.
|
||||||
|
|
|
||||||
|
|
@ -3105,7 +3105,7 @@ public class Tethering {
|
||||||
|
|
||||||
mLog.i("adding IpServer for: " + iface);
|
mLog.i("adding IpServer for: " + iface);
|
||||||
final TetherState tetherState = new TetherState(
|
final TetherState tetherState = new TetherState(
|
||||||
new IpServer(iface, mHandler, interfaceType, mLog, mNetd, mBpfCoordinator,
|
new IpServer(iface, mContext, mHandler, interfaceType, mLog, mNetd, mBpfCoordinator,
|
||||||
mRoutingCoordinator, new ControlCallback(), mConfig, mTetheringMetrics,
|
mRoutingCoordinator, new ControlCallback(), mConfig, mTetheringMetrics,
|
||||||
mDeps.makeIpServerDependencies()), isNcm);
|
mDeps.makeIpServerDependencies()), isNcm);
|
||||||
mTetherStates.put(iface, tetherState);
|
mTetherStates.put(iface, tetherState);
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,12 @@ public class TetheringConfiguration {
|
||||||
*/
|
*/
|
||||||
public static final String TETHER_ACTIVE_SESSIONS_METRICS = "tether_active_sessions_metrics";
|
public static final String TETHER_ACTIVE_SESSIONS_METRICS = "tether_active_sessions_metrics";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A feature flag to control whether the tethering local network agent should be enabled.
|
||||||
|
* Disabled by default.
|
||||||
|
*/
|
||||||
|
public static final String TETHERING_LOCAL_NETWORK_AGENT = "tethering_local_network_agent";
|
||||||
|
|
||||||
public final String[] tetherableUsbRegexs;
|
public final String[] tetherableUsbRegexs;
|
||||||
public final String[] tetherableWifiRegexs;
|
public final String[] tetherableWifiRegexs;
|
||||||
public final String[] tetherableWigigRegexs;
|
public final String[] tetherableWigigRegexs;
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,20 @@
|
||||||
*/
|
*/
|
||||||
package com.android.networkstack.tethering.util;
|
package com.android.networkstack.tethering.util;
|
||||||
|
|
||||||
|
import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
|
||||||
|
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
|
||||||
|
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
||||||
|
import static android.net.NetworkCapabilities.TRANSPORT_USB;
|
||||||
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
|
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
|
||||||
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
|
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
|
||||||
|
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
|
||||||
|
import static android.net.TetheringManager.TETHERING_ETHERNET;
|
||||||
|
import static android.net.TetheringManager.TETHERING_NCM;
|
||||||
|
import static android.net.TetheringManager.TETHERING_USB;
|
||||||
|
import static android.net.TetheringManager.TETHERING_VIRTUAL;
|
||||||
|
import static android.net.TetheringManager.TETHERING_WIFI;
|
||||||
|
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
|
||||||
|
import static android.net.TetheringManager.TETHERING_WIGIG;
|
||||||
|
|
||||||
import android.net.TetherStatsParcel;
|
import android.net.TetherStatsParcel;
|
||||||
import android.net.TetheringManager.TetheringRequest;
|
import android.net.TetheringManager.TetheringRequest;
|
||||||
|
|
@ -207,4 +219,30 @@ public class TetheringUtils {
|
||||||
request.getParcel().connectivityScope = CONNECTIVITY_SCOPE_GLOBAL;
|
request.getParcel().connectivityScope = CONNECTIVITY_SCOPE_GLOBAL;
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the transport type for the given interface type.
|
||||||
|
*
|
||||||
|
* @param interfaceType The interface type.
|
||||||
|
* @return The transport type.
|
||||||
|
* @throws IllegalArgumentException if the interface type is invalid.
|
||||||
|
*/
|
||||||
|
public static int getTransportTypeForTetherableType(int interfaceType) {
|
||||||
|
switch (interfaceType) {
|
||||||
|
case TETHERING_WIFI:
|
||||||
|
case TETHERING_WIGIG:
|
||||||
|
case TETHERING_WIFI_P2P:
|
||||||
|
return TRANSPORT_WIFI;
|
||||||
|
case TETHERING_USB:
|
||||||
|
case TETHERING_NCM:
|
||||||
|
return TRANSPORT_USB;
|
||||||
|
case TETHERING_BLUETOOTH:
|
||||||
|
return TRANSPORT_BLUETOOTH;
|
||||||
|
case TETHERING_ETHERNET:
|
||||||
|
case TETHERING_VIRTUAL: // For virtual machines.
|
||||||
|
return TRANSPORT_ETHERNET;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid interface type: " + interfaceType);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ package android.net;
|
||||||
|
|
||||||
import static android.Manifest.permission.DUMP;
|
import static android.Manifest.permission.DUMP;
|
||||||
import static android.net.InetAddresses.parseNumericAddress;
|
import static android.net.InetAddresses.parseNumericAddress;
|
||||||
|
import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK;
|
||||||
|
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
|
||||||
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
|
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
|
||||||
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
|
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
|
||||||
import static android.net.TetheringManager.TETHERING_ETHERNET;
|
import static android.net.TetheringManager.TETHERING_ETHERNET;
|
||||||
|
|
@ -27,6 +29,7 @@ import static android.net.TetheringTester.buildIcmpEchoPacketV4;
|
||||||
import static android.net.TetheringTester.buildUdpPacket;
|
import static android.net.TetheringTester.buildUdpPacket;
|
||||||
import static android.net.TetheringTester.isExpectedIcmpPacket;
|
import static android.net.TetheringTester.isExpectedIcmpPacket;
|
||||||
import static android.net.TetheringTester.isExpectedUdpDnsPacket;
|
import static android.net.TetheringTester.isExpectedUdpDnsPacket;
|
||||||
|
import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
|
||||||
import static android.system.OsConstants.ICMP_ECHO;
|
import static android.system.OsConstants.ICMP_ECHO;
|
||||||
import static android.system.OsConstants.ICMP_ECHOREPLY;
|
import static android.system.OsConstants.ICMP_ECHOREPLY;
|
||||||
import static android.system.OsConstants.IPPROTO_UDP;
|
import static android.system.OsConstants.IPPROTO_UDP;
|
||||||
|
|
@ -78,12 +81,16 @@ import com.android.net.module.util.bpf.TetherStatsKey;
|
||||||
import com.android.net.module.util.bpf.TetherStatsValue;
|
import com.android.net.module.util.bpf.TetherStatsValue;
|
||||||
import com.android.net.module.util.structs.Ipv4Header;
|
import com.android.net.module.util.structs.Ipv4Header;
|
||||||
import com.android.net.module.util.structs.UdpHeader;
|
import com.android.net.module.util.structs.UdpHeader;
|
||||||
|
import com.android.testutils.AutoReleaseNetworkCallbackRule;
|
||||||
import com.android.testutils.DevSdkIgnoreRule;
|
import com.android.testutils.DevSdkIgnoreRule;
|
||||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
|
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
|
||||||
|
import com.android.testutils.DeviceConfigRule;
|
||||||
import com.android.testutils.DeviceInfoUtils;
|
import com.android.testutils.DeviceInfoUtils;
|
||||||
import com.android.testutils.DumpTestUtils;
|
import com.android.testutils.DumpTestUtils;
|
||||||
import com.android.testutils.NetworkStackModuleTest;
|
import com.android.testutils.NetworkStackModuleTest;
|
||||||
import com.android.testutils.PollPacketReader;
|
import com.android.testutils.PollPacketReader;
|
||||||
|
import com.android.testutils.RecorderCallback.CallbackEntry;
|
||||||
|
import com.android.testutils.TestableNetworkCallback;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
|
@ -111,6 +118,12 @@ import java.util.concurrent.TimeoutException;
|
||||||
public class EthernetTetheringTest extends EthernetTetheringTestBase {
|
public class EthernetTetheringTest extends EthernetTetheringTestBase {
|
||||||
@Rule
|
@Rule
|
||||||
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
|
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
|
||||||
|
// For manipulating feature flag before and after testing.
|
||||||
|
@Rule
|
||||||
|
public final DeviceConfigRule mDeviceConfigRule = new DeviceConfigRule();
|
||||||
|
@Rule
|
||||||
|
public final AutoReleaseNetworkCallbackRule
|
||||||
|
mNetworkCallbackRule = new AutoReleaseNetworkCallbackRule();
|
||||||
|
|
||||||
private static final String TAG = EthernetTetheringTest.class.getSimpleName();
|
private static final String TAG = EthernetTetheringTest.class.getSimpleName();
|
||||||
|
|
||||||
|
|
@ -200,6 +213,9 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase {
|
||||||
(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04 /* Address: 1.2.3.4 */
|
(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04 /* Address: 1.2.3.4 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Shamelessly copied from TetheringConfiguration.
|
||||||
|
private static final String TETHERING_LOCAL_NETWORK_AGENT = "tethering_local_network_agent";
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
super.tearDown();
|
super.tearDown();
|
||||||
|
|
@ -1224,4 +1240,44 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase {
|
||||||
maybeUnregisterTetheringEventCallback(tetheringEventCallback);
|
maybeUnregisterTetheringEventCallback(tetheringEventCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||||
|
@Test
|
||||||
|
public void testLocalAgent_networkCallbacks() throws Exception {
|
||||||
|
mDeviceConfigRule.setConfig(NAMESPACE_TETHERING, TETHERING_LOCAL_NETWORK_AGENT, "1");
|
||||||
|
assumeFalse(isInterfaceForTetheringAvailable());
|
||||||
|
setIncludeTestInterfaces(true);
|
||||||
|
|
||||||
|
TestNetworkInterface downstreamIface = null;
|
||||||
|
MyTetheringEventCallback tetheringEventCallback = null;
|
||||||
|
|
||||||
|
final TestableNetworkCallback networkCallback = new TestableNetworkCallback();
|
||||||
|
final NetworkRequest networkRequest = new NetworkRequest.Builder()
|
||||||
|
.addCapability(NET_CAPABILITY_LOCAL_NETWORK).build();
|
||||||
|
mNetworkCallbackRule.registerNetworkCallback(networkRequest, networkCallback);
|
||||||
|
|
||||||
|
try {
|
||||||
|
downstreamIface = createTestInterface();
|
||||||
|
|
||||||
|
final String iface = mTetheredInterfaceRequester.getInterface();
|
||||||
|
assertEquals("TetheredInterfaceCallback for unexpected interface",
|
||||||
|
downstreamIface.getInterfaceName(), iface);
|
||||||
|
|
||||||
|
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_ETHERNET)
|
||||||
|
.setConnectivityScope(CONNECTIVITY_SCOPE_GLOBAL).build();
|
||||||
|
tetheringEventCallback = enableTethering(iface, request, null /* any upstream */);
|
||||||
|
tetheringEventCallback.awaitInterfaceTethered();
|
||||||
|
|
||||||
|
// Verify NetworkCallback works accordingly.
|
||||||
|
final Network network = networkCallback.expect(CallbackEntry.AVAILABLE).getNetwork();
|
||||||
|
final CallbackEntry.CapabilitiesChanged capEvent =
|
||||||
|
networkCallback.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED);
|
||||||
|
assertEquals(network, capEvent.getNetwork());
|
||||||
|
assertTrue(capEvent.getCaps().hasTransport(TRANSPORT_ETHERNET));
|
||||||
|
assertTrue(capEvent.getCaps().hasCapability(NET_CAPABILITY_LOCAL_NETWORK));
|
||||||
|
} finally {
|
||||||
|
stopEthernetTethering(tetheringEventCallback);
|
||||||
|
maybeCloseTestInterface(downstreamIface);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import static android.net.TetheringManager.TETHERING_WIFI;
|
||||||
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
|
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
|
import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
|
||||||
|
import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
|
||||||
import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
|
import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
|
||||||
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
|
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
|
||||||
import static android.net.ip.IpServer.STATE_AVAILABLE;
|
import static android.net.ip.IpServer.STATE_AVAILABLE;
|
||||||
|
|
@ -37,7 +38,9 @@ import static android.net.ip.IpServer.STATE_UNAVAILABLE;
|
||||||
import static android.net.ip.IpServer.getTetherableIpv6Prefixes;
|
import static android.net.ip.IpServer.getTetherableIpv6Prefixes;
|
||||||
|
|
||||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
|
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
|
||||||
|
import static com.android.modules.utils.build.SdkLevel.isAtLeastV;
|
||||||
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
|
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
|
||||||
|
import static com.android.networkstack.tethering.TetheringConfiguration.TETHERING_LOCAL_NETWORK_AGENT;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|
@ -53,6 +56,7 @@ import static org.mockito.Matchers.eq;
|
||||||
import static org.mockito.Mockito.clearInvocations;
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.doThrow;
|
import static org.mockito.Mockito.doThrow;
|
||||||
import static org.mockito.Mockito.inOrder;
|
import static org.mockito.Mockito.inOrder;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
@ -64,7 +68,7 @@ import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.app.usage.NetworkStatsManager;
|
import android.content.Context;
|
||||||
import android.net.INetd;
|
import android.net.INetd;
|
||||||
import android.net.InetAddresses;
|
import android.net.InetAddresses;
|
||||||
import android.net.InterfaceConfigurationParcel;
|
import android.net.InterfaceConfigurationParcel;
|
||||||
|
|
@ -72,6 +76,8 @@ import android.net.IpPrefix;
|
||||||
import android.net.LinkAddress;
|
import android.net.LinkAddress;
|
||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.MacAddress;
|
import android.net.MacAddress;
|
||||||
|
import android.net.Network;
|
||||||
|
import android.net.NetworkAgent;
|
||||||
import android.net.RouteInfo;
|
import android.net.RouteInfo;
|
||||||
import android.net.TetheringManager.TetheringRequest;
|
import android.net.TetheringManager.TetheringRequest;
|
||||||
import android.net.dhcp.DhcpServerCallbacks;
|
import android.net.dhcp.DhcpServerCallbacks;
|
||||||
|
|
@ -83,8 +89,10 @@ import android.net.ip.RouterAdvertisementDaemon.RaParams;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.os.ServiceSpecificException;
|
||||||
import android.os.test.TestLooper;
|
import android.os.test.TestLooper;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.ArrayMap;
|
||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
@ -100,6 +108,7 @@ import com.android.networkstack.tethering.util.PrefixUtils;
|
||||||
import com.android.testutils.DevSdkIgnoreRule;
|
import com.android.testutils.DevSdkIgnoreRule;
|
||||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
|
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
|
||||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
|
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
|
||||||
|
import com.android.testutils.com.android.testutils.SetFeatureFlagsRule;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
|
@ -122,6 +131,16 @@ public class IpServerTest {
|
||||||
@Rule
|
@Rule
|
||||||
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
|
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
|
||||||
|
|
||||||
|
final ArrayMap<String, Boolean> mFeatureFlags = new ArrayMap<>();
|
||||||
|
// This will set feature flags from @FeatureFlag annotations
|
||||||
|
// into the map before setUp() runs.
|
||||||
|
@Rule
|
||||||
|
public final SetFeatureFlagsRule mSetFeatureFlagsRule =
|
||||||
|
new SetFeatureFlagsRule((name, enabled) -> {
|
||||||
|
mFeatureFlags.put(name, enabled);
|
||||||
|
return null;
|
||||||
|
}, (name) -> mFeatureFlags.getOrDefault(name, false));
|
||||||
|
|
||||||
private static final String IFACE_NAME = "testnet1";
|
private static final String IFACE_NAME = "testnet1";
|
||||||
private static final String UPSTREAM_IFACE = "upstream0";
|
private static final String UPSTREAM_IFACE = "upstream0";
|
||||||
private static final String UPSTREAM_IFACE2 = "upstream1";
|
private static final String UPSTREAM_IFACE2 = "upstream1";
|
||||||
|
|
@ -164,6 +183,7 @@ public class IpServerTest {
|
||||||
new LinkAddress("2001:db8:0:abcd::168/64"));
|
new LinkAddress("2001:db8:0:abcd::168/64"));
|
||||||
private static final Set<IpPrefix> UPSTREAM_PREFIXES2 = Set.of(
|
private static final Set<IpPrefix> UPSTREAM_PREFIXES2 = Set.of(
|
||||||
new IpPrefix("2001:db8:0:1234::/64"), new IpPrefix("2001:db8:0:abcd::/64"));
|
new IpPrefix("2001:db8:0:1234::/64"), new IpPrefix("2001:db8:0:abcd::/64"));
|
||||||
|
private static final int TEST_NET_ID = 123;
|
||||||
|
|
||||||
@Mock private INetd mNetd;
|
@Mock private INetd mNetd;
|
||||||
@Mock private IpServer.Callback mCallback;
|
@Mock private IpServer.Callback mCallback;
|
||||||
|
|
@ -173,10 +193,11 @@ public class IpServerTest {
|
||||||
@Mock private RouterAdvertisementDaemon mRaDaemon;
|
@Mock private RouterAdvertisementDaemon mRaDaemon;
|
||||||
@Mock private IpServer.Dependencies mDependencies;
|
@Mock private IpServer.Dependencies mDependencies;
|
||||||
@Mock private RoutingCoordinatorManager mRoutingCoordinatorManager;
|
@Mock private RoutingCoordinatorManager mRoutingCoordinatorManager;
|
||||||
@Mock private NetworkStatsManager mStatsManager;
|
|
||||||
@Mock private TetheringConfiguration mTetherConfig;
|
@Mock private TetheringConfiguration mTetherConfig;
|
||||||
@Mock private TetheringMetrics mTetheringMetrics;
|
@Mock private TetheringMetrics mTetheringMetrics;
|
||||||
@Mock private BpfCoordinator mBpfCoordinator;
|
@Mock private BpfCoordinator mBpfCoordinator;
|
||||||
|
@Mock private Context mContext;
|
||||||
|
@Mock private NetworkAgent mNetworkAgent;
|
||||||
|
|
||||||
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
|
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
|
||||||
|
|
||||||
|
|
@ -205,6 +226,18 @@ public class IpServerTest {
|
||||||
when(mDependencies.getInterfaceParams(UPSTREAM_IFACE)).thenReturn(UPSTREAM_IFACE_PARAMS);
|
when(mDependencies.getInterfaceParams(UPSTREAM_IFACE)).thenReturn(UPSTREAM_IFACE_PARAMS);
|
||||||
when(mDependencies.getInterfaceParams(UPSTREAM_IFACE2)).thenReturn(UPSTREAM_IFACE_PARAMS2);
|
when(mDependencies.getInterfaceParams(UPSTREAM_IFACE2)).thenReturn(UPSTREAM_IFACE_PARAMS2);
|
||||||
when(mDependencies.getInterfaceParams(IPSEC_IFACE)).thenReturn(IPSEC_IFACE_PARAMS);
|
when(mDependencies.getInterfaceParams(IPSEC_IFACE)).thenReturn(IPSEC_IFACE_PARAMS);
|
||||||
|
doAnswer(
|
||||||
|
invocation -> mFeatureFlags.getOrDefault((String) invocation.getArgument(1), false)
|
||||||
|
).when(mDependencies).isFeatureEnabled(any(), anyString());
|
||||||
|
if (isAtLeastV()) {
|
||||||
|
when(mDependencies.makeNetworkAgent(any(), any(), anyString(), anyInt(), any()))
|
||||||
|
.thenReturn(mNetworkAgent);
|
||||||
|
// Mock the returned network and modifying the status.
|
||||||
|
final Network network = mock(Network.class);
|
||||||
|
doReturn(TEST_NET_ID).when(network).getNetId();
|
||||||
|
doReturn(network).when(mNetworkAgent).register();
|
||||||
|
doReturn(network).when(mNetworkAgent).getNetwork();
|
||||||
|
}
|
||||||
|
|
||||||
mInterfaceConfiguration = new InterfaceConfigurationParcel();
|
mInterfaceConfiguration = new InterfaceConfigurationParcel();
|
||||||
mInterfaceConfiguration.flags = new String[0];
|
mInterfaceConfiguration.flags = new String[0];
|
||||||
|
|
@ -294,10 +327,9 @@ public class IpServerTest {
|
||||||
private IpServer createIpServer(final int interfaceType) {
|
private IpServer createIpServer(final int interfaceType) {
|
||||||
mLooper = new TestLooper();
|
mLooper = new TestLooper();
|
||||||
mHandler = new Handler(mLooper.getLooper());
|
mHandler = new Handler(mLooper.getLooper());
|
||||||
return new IpServer(IFACE_NAME, mHandler, interfaceType, mSharedLog, mNetd, mBpfCoordinator,
|
return new IpServer(IFACE_NAME, mContext, mHandler, interfaceType, mSharedLog, mNetd,
|
||||||
mRoutingCoordinatorManager, mCallback, mTetherConfig,
|
mBpfCoordinator, mRoutingCoordinatorManager, mCallback, mTetherConfig,
|
||||||
mTetheringMetrics, mDependencies);
|
mTetheringMetrics, mDependencies);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -342,6 +374,10 @@ public class IpServerTest {
|
||||||
verifyNoMoreInteractions(mNetd, mCallback);
|
verifyNoMoreInteractions(mNetd, mCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isTetheringNetworkAgentFeatureEnabled() {
|
||||||
|
return isAtLeastV() && mFeatureFlags.getOrDefault(TETHERING_LOCAL_NETWORK_AGENT, false);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void canBeTetheredAsBluetooth() throws Exception {
|
public void canBeTetheredAsBluetooth() throws Exception {
|
||||||
initStateMachine(TETHERING_BLUETOOTH);
|
initStateMachine(TETHERING_BLUETOOTH);
|
||||||
|
|
@ -360,10 +396,16 @@ public class IpServerTest {
|
||||||
IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
|
IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
|
||||||
}
|
}
|
||||||
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
|
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
|
||||||
inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||||
// One for ipv4 route, one for ipv6 link local route.
|
inOrder.verify(mNetd, never()).networkAddInterface(anyInt(), anyString());
|
||||||
inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
|
inOrder.verify(mNetd, never())
|
||||||
any(), any());
|
.networkAddRoute(anyInt(), anyString(), anyString(), anyString());
|
||||||
|
} else {
|
||||||
|
inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||||
|
// One for ipv4 route, one for ipv6 link local route.
|
||||||
|
inOrder.verify(mNetd, times(2))
|
||||||
|
.networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), any(), any());
|
||||||
|
}
|
||||||
inOrder.verify(mCallback).updateInterfaceState(
|
inOrder.verify(mCallback).updateInterfaceState(
|
||||||
mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
|
mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
|
||||||
inOrder.verify(mCallback).updateLinkProperties(
|
inOrder.verify(mCallback).updateLinkProperties(
|
||||||
|
|
@ -379,7 +421,11 @@ public class IpServerTest {
|
||||||
InOrder inOrder = inOrder(mCallback, mNetd, mRoutingCoordinatorManager);
|
InOrder inOrder = inOrder(mCallback, mNetd, mRoutingCoordinatorManager);
|
||||||
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
|
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
|
||||||
inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
|
inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
|
||||||
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||||
|
inOrder.verify(mNetd, never()).networkRemoveInterface(anyInt(), anyString());
|
||||||
|
} else {
|
||||||
|
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||||
|
}
|
||||||
// One is ipv4 address clear (set to 0.0.0.0), another is set interface down which only
|
// One is ipv4 address clear (set to 0.0.0.0), another is set interface down which only
|
||||||
// happen after T. Before T, the interface configuration control in bluetooth side.
|
// happen after T. Before T, the interface configuration control in bluetooth side.
|
||||||
if (isAtLeastT()) {
|
if (isAtLeastT()) {
|
||||||
|
|
@ -411,9 +457,15 @@ public class IpServerTest {
|
||||||
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
|
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
|
||||||
IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
|
IFACE_NAME.equals(cfg.ifName) && assertContainsFlag(cfg.flags, IF_STATE_UP)));
|
||||||
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
|
inOrder.verify(mNetd).tetherInterfaceAdd(IFACE_NAME);
|
||||||
inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||||
inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
|
inOrder.verify(mNetd, never()).networkAddInterface(anyInt(), anyString());
|
||||||
any(), any());
|
inOrder.verify(mNetd, never())
|
||||||
|
.networkAddRoute(anyInt(), anyString(), anyString(), anyString());
|
||||||
|
} else {
|
||||||
|
inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||||
|
inOrder.verify(mNetd, times(2))
|
||||||
|
.networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME), any(), any());
|
||||||
|
}
|
||||||
inOrder.verify(mCallback).updateInterfaceState(
|
inOrder.verify(mCallback).updateInterfaceState(
|
||||||
mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
|
mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
|
||||||
inOrder.verify(mCallback).updateLinkProperties(
|
inOrder.verify(mCallback).updateLinkProperties(
|
||||||
|
|
@ -592,7 +644,11 @@ public class IpServerTest {
|
||||||
inOrder.verify(mBpfCoordinator).clearAllIpv6Rules(mIpServer);
|
inOrder.verify(mBpfCoordinator).clearAllIpv6Rules(mIpServer);
|
||||||
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
|
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
|
||||||
inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
|
inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
|
||||||
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||||
|
inOrder.verify(mNetd, never()).networkRemoveInterface(anyInt(), anyString());
|
||||||
|
} else {
|
||||||
|
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||||
|
}
|
||||||
inOrder.verify(mNetd, times(isAtLeastT() ? 2 : 1)).interfaceSetCfg(
|
inOrder.verify(mNetd, times(isAtLeastT() ? 2 : 1)).interfaceSetCfg(
|
||||||
argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
|
argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
|
||||||
inOrder.verify(mRoutingCoordinatorManager).releaseDownstream(any());
|
inOrder.verify(mRoutingCoordinatorManager).releaseDownstream(any());
|
||||||
|
|
@ -1058,6 +1114,162 @@ public class IpServerTest {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||||
|
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT)
|
||||||
|
@Test
|
||||||
|
public void testTetheringNetworkAgent_tetheringAgentEnabled() throws Exception {
|
||||||
|
doTestTetheringNetworkAgent(CONNECTIVITY_SCOPE_GLOBAL, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||||
|
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT, enabled = false)
|
||||||
|
@Test
|
||||||
|
public void testTetheringNetworkAgent_tetheringAgentDisabled() throws Exception {
|
||||||
|
doTestTetheringNetworkAgent(CONNECTIVITY_SCOPE_GLOBAL, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify Tethering Network Agent feature doesn't affect Wi-fi P2P Group Owner although
|
||||||
|
// the code is mostly shared.
|
||||||
|
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||||
|
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT)
|
||||||
|
@Test
|
||||||
|
public void testTetheringNetworkAgent_p2pGroupOwnerAgentDisabled() throws Exception {
|
||||||
|
doTestTetheringNetworkAgent(CONNECTIVITY_SCOPE_LOCAL, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doTestTetheringNetworkAgent(int scope, boolean expectAgentEnabled)
|
||||||
|
throws Exception {
|
||||||
|
initStateMachine(TETHERING_USB);
|
||||||
|
|
||||||
|
final InOrder inOrder = inOrder(mNetworkAgent, mNetd);
|
||||||
|
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED,
|
||||||
|
0, createMockTetheringRequest(scope));
|
||||||
|
|
||||||
|
inOrder.verify(mNetworkAgent, expectAgentEnabled ? times(1) : never()).register();
|
||||||
|
inOrder.verify(mNetd, times(1)).tetherInterfaceAdd(anyString());
|
||||||
|
if (expectAgentEnabled) {
|
||||||
|
inOrder.verify(mNetd, never()).networkAddInterface(anyInt(), anyString());
|
||||||
|
inOrder.verify(mNetd, never()).networkAddRoute(anyInt(), anyString(), any(), any());
|
||||||
|
inOrder.verify(mNetworkAgent, times(1)).sendLinkProperties(any());
|
||||||
|
inOrder.verify(mNetworkAgent, times(1)).markConnected();
|
||||||
|
} else {
|
||||||
|
inOrder.verify(mNetd, times(1)).networkAddInterface(anyInt(), anyString());
|
||||||
|
inOrder.verify(mNetd, times(2)).networkAddRoute(anyInt(), anyString(), any(), any());
|
||||||
|
inOrder.verify(mNetworkAgent, never()).sendLinkProperties(any());
|
||||||
|
inOrder.verify(mNetworkAgent, never()).markConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
|
||||||
|
if (expectAgentEnabled) {
|
||||||
|
inOrder.verify(mNetworkAgent, times(1)).unregister();
|
||||||
|
inOrder.verify(mNetd, never()).networkRemoveInterface(anyInt(), anyString());
|
||||||
|
} else {
|
||||||
|
inOrder.verify(mNetworkAgent, never()).unregister();
|
||||||
|
inOrder.verify(mNetd, times(1)).networkRemoveInterface(anyInt(), anyString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify if the registration failed, tethering can be gracefully shutdown.
|
||||||
|
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||||
|
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT)
|
||||||
|
@Test
|
||||||
|
public void testTetheringNetworkAgent_registerThrows() throws Exception {
|
||||||
|
initStateMachine(TETHERING_USB);
|
||||||
|
|
||||||
|
final InOrder inOrder = inOrder(mNetworkAgent, mNetd, mCallback);
|
||||||
|
doReturn(null).when(mNetworkAgent).getNetwork();
|
||||||
|
doThrow(IllegalStateException.class).when(mNetworkAgent).register();
|
||||||
|
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED,
|
||||||
|
0, createMockTetheringRequest(CONNECTIVITY_SCOPE_GLOBAL));
|
||||||
|
|
||||||
|
inOrder.verify(mNetworkAgent).register();
|
||||||
|
inOrder.verify(mNetd, never()).networkCreate(any());
|
||||||
|
inOrder.verify(mNetworkAgent, never()).sendLinkProperties(any());
|
||||||
|
inOrder.verify(mNetworkAgent, never()).markConnected();
|
||||||
|
inOrder.verify(mNetworkAgent, never()).unregister();
|
||||||
|
inOrder.verify(mNetd, never()).networkDestroy(anyInt());
|
||||||
|
inOrder.verify(mCallback).updateInterfaceState(
|
||||||
|
mIpServer, STATE_AVAILABLE, TETHER_ERROR_SERVICE_UNAVAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify if the network creation failed, tethering can be gracefully shutdown.
|
||||||
|
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||||
|
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT)
|
||||||
|
@Test
|
||||||
|
public void testTetheringNetworkAgent_netdThrows() throws Exception {
|
||||||
|
initStateMachine(TETHERING_USB);
|
||||||
|
|
||||||
|
final InOrder inOrder = inOrder(mNetworkAgent, mNetd, mCallback);
|
||||||
|
doThrow(ServiceSpecificException.class).when(mNetd).tetherInterfaceAdd(any());
|
||||||
|
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED,
|
||||||
|
0, createMockTetheringRequest(CONNECTIVITY_SCOPE_GLOBAL));
|
||||||
|
|
||||||
|
inOrder.verify(mNetworkAgent).register();
|
||||||
|
inOrder.verify(mNetd, never()).networkCreate(any());
|
||||||
|
inOrder.verify(mNetworkAgent, never()).sendLinkProperties(any());
|
||||||
|
inOrder.verify(mNetworkAgent, never()).markConnected();
|
||||||
|
inOrder.verify(mNetworkAgent).unregister();
|
||||||
|
inOrder.verify(mNetd, never()).networkDestroy(anyInt());
|
||||||
|
inOrder.verify(mCallback).updateInterfaceState(
|
||||||
|
mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify when IPv6 address update, set routes accordingly.
|
||||||
|
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||||
|
@SetFeatureFlagsRule.FeatureFlag(name = TETHERING_LOCAL_NETWORK_AGENT)
|
||||||
|
@Test
|
||||||
|
public void testTetheringNetworkAgent_ipv6AddressUpdate() throws Exception {
|
||||||
|
initStateMachine(TETHERING_USB);
|
||||||
|
|
||||||
|
final InOrder inOrder = inOrder(mNetworkAgent, mNetd);
|
||||||
|
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED,
|
||||||
|
0, createMockTetheringRequest(CONNECTIVITY_SCOPE_GLOBAL));
|
||||||
|
|
||||||
|
inOrder.verify(mNetworkAgent).register();
|
||||||
|
inOrder.verify(mNetd, never()).networkCreate(any());
|
||||||
|
inOrder.verify(mNetd, never()).networkAddRoute(anyInt(), anyString(), any(), any());
|
||||||
|
|
||||||
|
// Ipv6 link local route won't show up in the LinkProperties, so just
|
||||||
|
// verify ipv4 route.
|
||||||
|
final ArgumentCaptor<LinkProperties> lpCaptor =
|
||||||
|
ArgumentCaptor.forClass(LinkProperties.class);
|
||||||
|
inOrder.verify(mNetworkAgent).sendLinkProperties(lpCaptor.capture());
|
||||||
|
final RouteInfo expectedIpv4Route = new RouteInfo(PrefixUtils.asIpPrefix(mTestAddress),
|
||||||
|
null, IFACE_NAME, RouteInfo.RTN_UNICAST);
|
||||||
|
assertRoutes(List.of(expectedIpv4Route), lpCaptor.getValue().getRoutes());
|
||||||
|
assertEquals(IFACE_NAME, lpCaptor.getValue().getInterfaceName());
|
||||||
|
|
||||||
|
inOrder.verify(mNetworkAgent).markConnected();
|
||||||
|
|
||||||
|
// Mock ipv4-only upstream show up.
|
||||||
|
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
|
||||||
|
inOrder.verifyNoMoreInteractions();
|
||||||
|
|
||||||
|
// Verify LinkProperties is updated when IPv6 connectivity is available.
|
||||||
|
final LinkProperties lp = new LinkProperties();
|
||||||
|
lp.setInterfaceName(UPSTREAM_IFACE);
|
||||||
|
lp.setLinkAddresses(UPSTREAM_ADDRESSES);
|
||||||
|
dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
|
||||||
|
inOrder.verify(mNetd, never()).networkAddRoute(anyInt(), anyString(), any(), any());
|
||||||
|
inOrder.verify(mNetworkAgent).sendLinkProperties(lpCaptor.capture());
|
||||||
|
|
||||||
|
// Expect one Ipv4 route, plus one Ipv6 route.
|
||||||
|
final RouteInfo expectedIpv6Route = new RouteInfo(UPSTREAM_PREFIXES.toArray(
|
||||||
|
new IpPrefix[0])[0], null, IFACE_NAME, RouteInfo.RTN_UNICAST);
|
||||||
|
assertRoutes(List.of(expectedIpv4Route, expectedIpv6Route),
|
||||||
|
lpCaptor.getValue().getRoutes());
|
||||||
|
assertEquals(IFACE_NAME, lpCaptor.getValue().getInterfaceName());
|
||||||
|
|
||||||
|
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
|
||||||
|
inOrder.verify(mNetworkAgent).unregister();
|
||||||
|
inOrder.verify(mNetd, never()).networkDestroy(anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertRoutes(List<RouteInfo> expectedRoutes, List<RouteInfo> actualRoutes) {
|
||||||
|
assertTrue("Expected Routes: " + expectedRoutes + ", but got: " + actualRoutes,
|
||||||
|
expectedRoutes.equals(actualRoutes));
|
||||||
|
}
|
||||||
|
|
||||||
@Test @IgnoreUpTo(Build.VERSION_CODES.R)
|
@Test @IgnoreUpTo(Build.VERSION_CODES.R)
|
||||||
public void dadProxyUpdates() throws Exception {
|
public void dadProxyUpdates() throws Exception {
|
||||||
InOrder inOrder = inOrder(mDadProxy);
|
InOrder inOrder = inOrder(mDadProxy);
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
||||||
|
|
||||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
|
import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
|
||||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
|
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
|
||||||
|
import static com.android.modules.utils.build.SdkLevel.isAtLeastV;
|
||||||
import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
|
import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
|
||||||
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
|
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
|
||||||
import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
|
import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
|
||||||
|
|
@ -83,6 +84,7 @@ import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOA
|
||||||
import static com.android.networkstack.tethering.TestConnectivityManager.BROADCAST_FIRST;
|
import static com.android.networkstack.tethering.TestConnectivityManager.BROADCAST_FIRST;
|
||||||
import static com.android.networkstack.tethering.TestConnectivityManager.CALLBACKS_FIRST;
|
import static com.android.networkstack.tethering.TestConnectivityManager.CALLBACKS_FIRST;
|
||||||
import static com.android.networkstack.tethering.Tethering.UserRestrictionActionListener;
|
import static com.android.networkstack.tethering.Tethering.UserRestrictionActionListener;
|
||||||
|
import static com.android.networkstack.tethering.TetheringConfiguration.TETHERING_LOCAL_NETWORK_AGENT;
|
||||||
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
|
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
|
||||||
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_NCM_FUNCTION;
|
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_NCM_FUNCTION;
|
||||||
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_RNDIS_FUNCTION;
|
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_RNDIS_FUNCTION;
|
||||||
|
|
@ -192,6 +194,7 @@ import android.telephony.CarrierConfigManager;
|
||||||
import android.telephony.PhoneStateListener;
|
import android.telephony.PhoneStateListener;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
import android.test.mock.MockContentResolver;
|
import android.test.mock.MockContentResolver;
|
||||||
|
import android.util.ArrayMap;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
@ -219,6 +222,7 @@ import com.android.testutils.DevSdkIgnoreRule;
|
||||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
|
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
|
||||||
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
|
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
|
||||||
import com.android.testutils.MiscAsserts;
|
import com.android.testutils.MiscAsserts;
|
||||||
|
import com.android.testutils.com.android.testutils.SetFeatureFlagsRule;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
@ -250,6 +254,16 @@ import java.util.concurrent.Executor;
|
||||||
public class TetheringTest {
|
public class TetheringTest {
|
||||||
@Rule public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
|
@Rule public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
|
||||||
|
|
||||||
|
final ArrayMap<String, Boolean> mFeatureFlags = new ArrayMap<>();
|
||||||
|
// This will set feature flags from @FeatureFlag annotations
|
||||||
|
// into the map before setUp() runs.
|
||||||
|
@Rule
|
||||||
|
public final SetFeatureFlagsRule mSetFeatureFlagsRule =
|
||||||
|
new SetFeatureFlagsRule((name, enabled) -> {
|
||||||
|
mFeatureFlags.put(name, enabled);
|
||||||
|
return null;
|
||||||
|
}, (name) -> mFeatureFlags.getOrDefault(name, false));
|
||||||
|
|
||||||
private static final int IFINDEX_OFFSET = 100;
|
private static final int IFINDEX_OFFSET = 100;
|
||||||
|
|
||||||
private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
|
private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
|
||||||
|
|
@ -461,6 +475,11 @@ public class TetheringTest {
|
||||||
public void setOnDhcpServerCreatedResult(final int result) {
|
public void setOnDhcpServerCreatedResult(final int result) {
|
||||||
mOnDhcpServerCreatedResult = result;
|
mOnDhcpServerCreatedResult = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFeatureEnabled(Context context, String name) {
|
||||||
|
return mFeatureFlags.getOrDefault(name, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MockTetheringDependencies extends TetheringDependencies {
|
public class MockTetheringDependencies extends TetheringDependencies {
|
||||||
|
|
@ -954,12 +973,18 @@ public class TetheringTest {
|
||||||
verifyNoMoreInteractions(mCm);
|
verifyNoMoreInteractions(mCm);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyInterfaceServingModeStarted(String ifname) throws Exception {
|
private void verifyInterfaceServingModeStarted(String ifname, boolean expectAgentEnabled)
|
||||||
|
throws Exception {
|
||||||
verify(mNetd).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
|
verify(mNetd).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
|
||||||
verify(mNetd).tetherInterfaceAdd(ifname);
|
verify(mNetd).tetherInterfaceAdd(ifname);
|
||||||
verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, ifname);
|
if (expectAgentEnabled) {
|
||||||
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(ifname),
|
verify(mNetd, never()).networkAddInterface(anyInt(), anyString());
|
||||||
anyString(), anyString());
|
verify(mNetd, never()).networkAddRoute(anyInt(), anyString(), anyString(), anyString());
|
||||||
|
} else {
|
||||||
|
verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, ifname);
|
||||||
|
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(ifname),
|
||||||
|
anyString(), anyString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyTetheringBroadcast(String ifname, String whichExtra) {
|
private void verifyTetheringBroadcast(String ifname, String whichExtra) {
|
||||||
|
|
@ -1061,10 +1086,18 @@ public class TetheringTest {
|
||||||
failingLocalOnlyHotspotLegacyApBroadcast(false);
|
failingLocalOnlyHotspotLegacyApBroadcast(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyStopHotpot() throws Exception {
|
private boolean isTetheringNetworkAgentFeatureEnabled() {
|
||||||
|
return isAtLeastV() && mFeatureFlags.getOrDefault(TETHERING_LOCAL_NETWORK_AGENT, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyStopHotpot(boolean isLocalOnly) throws Exception {
|
||||||
verify(mNetd).tetherApplyDnsInterfaces();
|
verify(mNetd).tetherApplyDnsInterfaces();
|
||||||
verify(mNetd).tetherInterfaceRemove(TEST_WLAN_IFNAME);
|
verify(mNetd).tetherInterfaceRemove(TEST_WLAN_IFNAME);
|
||||||
verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
|
if (!isLocalOnly && isTetheringNetworkAgentFeatureEnabled()) {
|
||||||
|
verify(mNetd, never()).networkRemoveInterface(anyInt(), anyString());
|
||||||
|
} else {
|
||||||
|
verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
|
||||||
|
}
|
||||||
// interfaceSetCfg() called once for enabling and twice disabling IPv4.
|
// interfaceSetCfg() called once for enabling and twice disabling IPv4.
|
||||||
verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
|
verify(mNetd, times(3)).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
|
||||||
verify(mNetd).tetherStop();
|
verify(mNetd).tetherStop();
|
||||||
|
|
@ -1083,7 +1116,8 @@ public class TetheringTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyStartHotspot(boolean isLocalOnly) throws Exception {
|
private void verifyStartHotspot(boolean isLocalOnly) throws Exception {
|
||||||
verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME);
|
final boolean expectAgentEnabled = !isLocalOnly && isTetheringNetworkAgentFeatureEnabled();
|
||||||
|
verifyInterfaceServingModeStarted(TEST_WLAN_IFNAME, expectAgentEnabled);
|
||||||
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
|
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
|
||||||
verify(mWifiManager).updateInterfaceIpState(
|
verify(mWifiManager).updateInterfaceIpState(
|
||||||
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
|
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
|
||||||
|
|
@ -1127,7 +1161,7 @@ public class TetheringTest {
|
||||||
mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
|
mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
|
||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
|
|
||||||
verifyStopHotpot();
|
verifyStopHotpot(true /* isLocalOnly */);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -2073,7 +2107,7 @@ public class TetheringTest {
|
||||||
mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
|
mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
|
||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
|
|
||||||
verifyStopHotpot();
|
verifyStopHotpot(false /* isLocalOnly */);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -2218,9 +2252,14 @@ public class TetheringTest {
|
||||||
// code is refactored the two calls during shutdown will revert to one.
|
// code is refactored the two calls during shutdown will revert to one.
|
||||||
verify(mNetd, times(3)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName)));
|
verify(mNetd, times(3)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName)));
|
||||||
verify(mNetd, times(1)).tetherInterfaceAdd(TEST_WLAN_IFNAME);
|
verify(mNetd, times(1)).tetherInterfaceAdd(TEST_WLAN_IFNAME);
|
||||||
verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
|
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||||
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME),
|
verify(mNetd, never()).networkAddInterface(anyInt(), anyString());
|
||||||
anyString(), anyString());
|
verify(mNetd, never()).networkAddRoute(anyInt(), anyString(), anyString(), anyString());
|
||||||
|
} else {
|
||||||
|
verify(mNetd, times(1)).networkAddInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
|
||||||
|
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_WLAN_IFNAME),
|
||||||
|
anyString(), anyString());
|
||||||
|
}
|
||||||
verify(mWifiManager).updateInterfaceIpState(
|
verify(mWifiManager).updateInterfaceIpState(
|
||||||
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
|
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
|
||||||
verify(mWifiManager).updateInterfaceIpState(
|
verify(mWifiManager).updateInterfaceIpState(
|
||||||
|
|
@ -2239,7 +2278,11 @@ public class TetheringTest {
|
||||||
// so it can take down AP mode.
|
// so it can take down AP mode.
|
||||||
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
|
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
|
||||||
verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME);
|
verify(mNetd, times(1)).tetherInterfaceRemove(TEST_WLAN_IFNAME);
|
||||||
verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
|
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||||
|
verify(mNetd, never()).networkRemoveInterface(anyInt(), anyString());
|
||||||
|
} else {
|
||||||
|
verify(mNetd, times(1)).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_WLAN_IFNAME);
|
||||||
|
}
|
||||||
verify(mWifiManager).updateInterfaceIpState(
|
verify(mWifiManager).updateInterfaceIpState(
|
||||||
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
|
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
|
||||||
|
|
||||||
|
|
@ -3100,7 +3143,7 @@ public class TetheringTest {
|
||||||
}
|
}
|
||||||
sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME);
|
sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME);
|
||||||
|
|
||||||
verifyInterfaceServingModeStarted(TEST_P2P_IFNAME);
|
verifyInterfaceServingModeStarted(TEST_P2P_IFNAME, false);
|
||||||
verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER);
|
verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER);
|
||||||
verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME);
|
verify(mNetd, times(1)).ipfwdEnableForwarding(TETHERING_NAME);
|
||||||
verify(mNetd, times(1)).tetherStartWithConfiguration(any());
|
verify(mNetd, times(1)).tetherStartWithConfiguration(any());
|
||||||
|
|
@ -4119,13 +4162,22 @@ public class TetheringTest {
|
||||||
&& assertContainsFlag(cfg.flags, INetd.IF_STATE_UP)));
|
&& assertContainsFlag(cfg.flags, INetd.IF_STATE_UP)));
|
||||||
}
|
}
|
||||||
verify(mNetd).tetherInterfaceAdd(TEST_BT_IFNAME);
|
verify(mNetd).tetherInterfaceAdd(TEST_BT_IFNAME);
|
||||||
verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
|
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||||
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_BT_IFNAME),
|
verify(mNetd, never()).networkAddInterface(anyInt(), anyString());
|
||||||
anyString(), anyString());
|
verify(mNetd, never()).networkAddRoute(anyInt(), anyString(), anyString(), anyString());
|
||||||
|
} else {
|
||||||
|
verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
|
||||||
|
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_BT_IFNAME),
|
||||||
|
anyString(), anyString());
|
||||||
|
}
|
||||||
verify(mNetd).ipfwdEnableForwarding(TETHERING_NAME);
|
verify(mNetd).ipfwdEnableForwarding(TETHERING_NAME);
|
||||||
verify(mNetd).tetherStartWithConfiguration(any());
|
verify(mNetd).tetherStartWithConfiguration(any());
|
||||||
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_BT_IFNAME),
|
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||||
anyString(), anyString());
|
verify(mNetd, never()).networkAddRoute(anyInt(), anyString(), anyString(), anyString());
|
||||||
|
} else {
|
||||||
|
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_BT_IFNAME),
|
||||||
|
anyString(), anyString());
|
||||||
|
}
|
||||||
verifyNoMoreInteractions(mNetd);
|
verifyNoMoreInteractions(mNetd);
|
||||||
reset(mNetd);
|
reset(mNetd);
|
||||||
}
|
}
|
||||||
|
|
@ -4140,7 +4192,11 @@ public class TetheringTest {
|
||||||
private void verifyNetdCommandForBtTearDown() throws Exception {
|
private void verifyNetdCommandForBtTearDown() throws Exception {
|
||||||
verify(mNetd).tetherApplyDnsInterfaces();
|
verify(mNetd).tetherApplyDnsInterfaces();
|
||||||
verify(mNetd).tetherInterfaceRemove(TEST_BT_IFNAME);
|
verify(mNetd).tetherInterfaceRemove(TEST_BT_IFNAME);
|
||||||
verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
|
if (isTetheringNetworkAgentFeatureEnabled()) {
|
||||||
|
verify(mNetd, never()).networkRemoveInterface(anyInt(), anyString());
|
||||||
|
} else {
|
||||||
|
verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
|
||||||
|
}
|
||||||
// One is ipv4 address clear (set to 0.0.0.0), another is set interface down which only
|
// One is ipv4 address clear (set to 0.0.0.0), another is set interface down which only
|
||||||
// happen after T. Before T, the interface configuration control in bluetooth side.
|
// happen after T. Before T, the interface configuration control in bluetooth side.
|
||||||
verify(mNetd, times(isAtLeastT() ? 2 : 1)).interfaceSetCfg(
|
verify(mNetd, times(isAtLeastT() ? 2 : 1)).interfaceSetCfg(
|
||||||
|
|
@ -4405,7 +4461,7 @@ public class TetheringTest {
|
||||||
initTetheringOnTestThread();
|
initTetheringOnTestThread();
|
||||||
// Enable wifi P2P.
|
// Enable wifi P2P.
|
||||||
sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME);
|
sendWifiP2pConnectionChanged(true, true, TEST_P2P_IFNAME);
|
||||||
verifyInterfaceServingModeStarted(TEST_P2P_IFNAME);
|
verifyInterfaceServingModeStarted(TEST_P2P_IFNAME, false);
|
||||||
verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER);
|
verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_AVAILABLE_TETHER);
|
||||||
verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
|
verifyTetheringBroadcast(TEST_P2P_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
|
||||||
verify(mUpstreamNetworkMonitor).startObserveUpstreamNetworks();
|
verify(mUpstreamNetworkMonitor).startObserveUpstreamNetworks();
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,25 @@
|
||||||
|
|
||||||
package android.net.connectivity;
|
package android.net.connectivity;
|
||||||
|
|
||||||
|
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
|
||||||
|
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
|
||||||
|
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
|
||||||
|
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.LinkProperties;
|
||||||
|
import android.net.LocalNetworkConfig;
|
||||||
|
import android.net.NetworkAgent;
|
||||||
|
import android.net.NetworkAgentConfig;
|
||||||
|
import android.net.NetworkCapabilities;
|
||||||
|
import android.net.NetworkScore;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -57,4 +71,36 @@ public class ConnectivityInternalApiUtil {
|
||||||
final ConnectivityManager cm = ctx.getSystemService(ConnectivityManager.class);
|
final ConnectivityManager cm = ctx.getSystemService(ConnectivityManager.class);
|
||||||
return cm.getRoutingCoordinatorService();
|
return cm.getRoutingCoordinatorService();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a NetworkAgent instance to be used by Tethering.
|
||||||
|
* @param ctx the context
|
||||||
|
* @return an instance of the {@code NetworkAgent}
|
||||||
|
*/
|
||||||
|
// TODO: Expose LocalNetworkConfig related APIs and delete this method. This method is
|
||||||
|
// only here because on R Tethering is installed and not Connectivity, requiring all
|
||||||
|
// shared classes to be public API. LocalNetworkConfig is not public yet, but it will
|
||||||
|
// only be used by Tethering on V+ so it's fine.
|
||||||
|
@SuppressLint("WrongConstant")
|
||||||
|
@NonNull
|
||||||
|
public static NetworkAgent buildTetheringNetworkAgent(@NonNull Context ctx,
|
||||||
|
@NonNull Looper looper, @NonNull String logTag, int transportType,
|
||||||
|
@NonNull LinkProperties lp) {
|
||||||
|
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
|
||||||
|
.addCapability(NET_CAPABILITY_NOT_METERED)
|
||||||
|
.addCapability(NET_CAPABILITY_NOT_ROAMING)
|
||||||
|
.addCapability(NET_CAPABILITY_NOT_CONGESTED)
|
||||||
|
.addCapability(NET_CAPABILITY_NOT_SUSPENDED)
|
||||||
|
.addTransportType(transportType);
|
||||||
|
// TODO: Change to use the constant definition. Flags.netCapabilityLocalNetwork() was not
|
||||||
|
// fully rolled out but the service will still process this capability, set it anyway.
|
||||||
|
builder.addCapability(36 /* NET_CAPABILITY_LOCAL_NETWORK */);
|
||||||
|
final NetworkCapabilities caps = builder.build();
|
||||||
|
final NetworkAgentConfig nac = new NetworkAgentConfig.Builder().build();
|
||||||
|
return new NetworkAgent(ctx, looper, logTag, caps, lp,
|
||||||
|
new LocalNetworkConfig.Builder().build(), new NetworkScore.Builder()
|
||||||
|
.setKeepConnectedReason(NetworkScore.KEEP_CONNECTED_LOCAL_NETWORK)
|
||||||
|
.build(), nac, null /* provider */) {
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue