Merge "Use new bluetooth API to enable/disable bt tethering after T"
This commit is contained in:
commit
ceeebae37e
5 changed files with 226 additions and 27 deletions
|
|
@ -614,10 +614,8 @@ public class IpServer extends StateMachine {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
|
if (shouldNotConfigureBluetoothInterface()) {
|
||||||
// BT configures the interface elsewhere: only start DHCP.
|
// Interface was already configured elsewhere, only start DHCP.
|
||||||
// TODO: make all tethering types behave the same way, and delete the bluetooth
|
|
||||||
// code that calls into NetworkManagementService directly.
|
|
||||||
return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);
|
return configureDhcp(enabled, mIpv4Address, null /* clientAddress */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -651,12 +649,15 @@ public class IpServer extends StateMachine {
|
||||||
return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
|
return configureDhcp(enabled, mIpv4Address, mStaticIpv4ClientAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean shouldNotConfigureBluetoothInterface() {
|
||||||
|
// Before T, bluetooth tethering configures the interface elsewhere.
|
||||||
|
return (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) && !SdkLevel.isAtLeastT();
|
||||||
|
}
|
||||||
|
|
||||||
private LinkAddress requestIpv4Address(final boolean useLastAddress) {
|
private LinkAddress requestIpv4Address(final boolean useLastAddress) {
|
||||||
if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr;
|
if (mStaticIpv4ServerAddr != null) return mStaticIpv4ServerAddr;
|
||||||
|
|
||||||
if (mInterfaceType == TetheringManager.TETHERING_BLUETOOTH) {
|
if (shouldNotConfigureBluetoothInterface()) return new LinkAddress(BLUETOOTH_IFACE_ADDR);
|
||||||
return new LinkAddress(BLUETOOTH_IFACE_ADDR);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mPrivateAddressCoordinator.requestDownstreamAddress(this, useLastAddress);
|
return mPrivateAddressCoordinator.requestDownstreamAddress(this, useLastAddress);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,12 @@ import com.android.internal.util.IndentingPrintWriter;
|
||||||
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.internal.util.StateMachine;
|
import com.android.internal.util.StateMachine;
|
||||||
|
import com.android.modules.utils.build.SdkLevel;
|
||||||
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
|
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
|
||||||
|
import com.android.networkstack.apishim.common.BluetoothPanShim;
|
||||||
|
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceCallbackShim;
|
||||||
|
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceRequestShim;
|
||||||
|
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
|
||||||
import com.android.networkstack.tethering.util.InterfaceSet;
|
import com.android.networkstack.tethering.util.InterfaceSet;
|
||||||
import com.android.networkstack.tethering.util.PrefixUtils;
|
import com.android.networkstack.tethering.util.PrefixUtils;
|
||||||
import com.android.networkstack.tethering.util.TetheringUtils;
|
import com.android.networkstack.tethering.util.TetheringUtils;
|
||||||
|
|
@ -265,8 +270,11 @@ public class Tethering {
|
||||||
private int mOffloadStatus = TETHER_HARDWARE_OFFLOAD_STOPPED;
|
private int mOffloadStatus = TETHER_HARDWARE_OFFLOAD_STOPPED;
|
||||||
|
|
||||||
private EthernetManager.TetheredInterfaceRequest mEthernetIfaceRequest;
|
private EthernetManager.TetheredInterfaceRequest mEthernetIfaceRequest;
|
||||||
|
private TetheredInterfaceRequestShim mBluetoothIfaceRequest;
|
||||||
private String mConfiguredEthernetIface;
|
private String mConfiguredEthernetIface;
|
||||||
|
private String mConfiguredBluetoothIface;
|
||||||
private EthernetCallback mEthernetCallback;
|
private EthernetCallback mEthernetCallback;
|
||||||
|
private TetheredInterfaceCallbackShim mBluetoothCallback;
|
||||||
private SettingsObserver mSettingsObserver;
|
private SettingsObserver mSettingsObserver;
|
||||||
private BluetoothPan mBluetoothPan;
|
private BluetoothPan mBluetoothPan;
|
||||||
private PanServiceListener mBluetoothPanListener;
|
private PanServiceListener mBluetoothPanListener;
|
||||||
|
|
@ -533,14 +541,16 @@ public class Tethering {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method needs to exist because TETHERING_BLUETOOTH and TETHERING_WIGIG can't use
|
// This method needs to exist because TETHERING_BLUETOOTH before Android T and TETHERING_WIGIG
|
||||||
// enableIpServing.
|
// can't use enableIpServing.
|
||||||
private void processInterfaceStateChange(final String iface, boolean enabled) {
|
private void processInterfaceStateChange(final String iface, boolean enabled) {
|
||||||
// Do not listen to USB interface state changes or USB interface add/removes. USB tethering
|
// Do not listen to USB interface state changes or USB interface add/removes. USB tethering
|
||||||
// is driven only by USB_ACTION broadcasts.
|
// is driven only by USB_ACTION broadcasts.
|
||||||
final int type = ifaceNameToType(iface);
|
final int type = ifaceNameToType(iface);
|
||||||
if (type == TETHERING_USB || type == TETHERING_NCM) return;
|
if (type == TETHERING_USB || type == TETHERING_NCM) return;
|
||||||
|
|
||||||
|
if (type == TETHERING_BLUETOOTH && SdkLevel.isAtLeastT()) return;
|
||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
ensureIpServerStarted(iface);
|
ensureIpServerStarted(iface);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -769,6 +779,9 @@ public class Tethering {
|
||||||
TETHERING_BLUETOOTH);
|
TETHERING_BLUETOOTH);
|
||||||
}
|
}
|
||||||
mPendingPanRequests.clear();
|
mPendingPanRequests.clear();
|
||||||
|
mBluetoothIfaceRequest = null;
|
||||||
|
mBluetoothCallback = null;
|
||||||
|
maybeDisableBluetoothIpServing();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -779,7 +792,11 @@ public class Tethering {
|
||||||
|
|
||||||
private void setBluetoothTetheringSettings(@NonNull final BluetoothPan bluetoothPan,
|
private void setBluetoothTetheringSettings(@NonNull final BluetoothPan bluetoothPan,
|
||||||
final boolean enable, final IIntResultListener listener) {
|
final boolean enable, final IIntResultListener listener) {
|
||||||
bluetoothPan.setBluetoothTethering(enable);
|
if (SdkLevel.isAtLeastT()) {
|
||||||
|
changeBluetoothTetheringSettings(bluetoothPan, enable);
|
||||||
|
} else {
|
||||||
|
changeBluetoothTetheringSettingsPreT(bluetoothPan, enable);
|
||||||
|
}
|
||||||
|
|
||||||
// Enabling bluetooth tethering settings can silently fail. Send internal error if the
|
// Enabling bluetooth tethering settings can silently fail. Send internal error if the
|
||||||
// result is not expected.
|
// result is not expected.
|
||||||
|
|
@ -788,6 +805,68 @@ public class Tethering {
|
||||||
sendTetherResult(listener, result, TETHERING_BLUETOOTH);
|
sendTetherResult(listener, result, TETHERING_BLUETOOTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void changeBluetoothTetheringSettingsPreT(@NonNull final BluetoothPan bluetoothPan,
|
||||||
|
final boolean enable) {
|
||||||
|
bluetoothPan.setBluetoothTethering(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void changeBluetoothTetheringSettings(@NonNull final BluetoothPan bluetoothPan,
|
||||||
|
final boolean enable) {
|
||||||
|
final BluetoothPanShim panShim = mDeps.getBluetoothPanShim(bluetoothPan);
|
||||||
|
if (enable) {
|
||||||
|
if (mBluetoothIfaceRequest != null) {
|
||||||
|
Log.d(TAG, "Bluetooth tethering settings already enabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mBluetoothCallback = new BluetoothCallback();
|
||||||
|
try {
|
||||||
|
mBluetoothIfaceRequest = panShim.requestTetheredInterface(mExecutor,
|
||||||
|
mBluetoothCallback);
|
||||||
|
} catch (UnsupportedApiLevelException e) {
|
||||||
|
Log.wtf(TAG, "Use unsupported API, " + e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mBluetoothIfaceRequest == null) {
|
||||||
|
Log.d(TAG, "Bluetooth tethering settings already disabled");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mBluetoothIfaceRequest.release();
|
||||||
|
mBluetoothIfaceRequest = null;
|
||||||
|
mBluetoothCallback = null;
|
||||||
|
// If bluetooth request is released, tethering won't able to receive
|
||||||
|
// onUnavailable callback, explicitly disable bluetooth IpServer manually.
|
||||||
|
maybeDisableBluetoothIpServing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BluetoothCallback is only called after T. Before T, PanService would call tether/untether to
|
||||||
|
// notify bluetooth interface status.
|
||||||
|
private class BluetoothCallback implements TetheredInterfaceCallbackShim {
|
||||||
|
@Override
|
||||||
|
public void onAvailable(String iface) {
|
||||||
|
if (this != mBluetoothCallback) return;
|
||||||
|
|
||||||
|
enableIpServing(TETHERING_BLUETOOTH, iface, getRequestedState(TETHERING_BLUETOOTH));
|
||||||
|
mConfiguredBluetoothIface = iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUnavailable() {
|
||||||
|
if (this != mBluetoothCallback) return;
|
||||||
|
|
||||||
|
maybeDisableBluetoothIpServing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeDisableBluetoothIpServing() {
|
||||||
|
if (mConfiguredBluetoothIface == null) return;
|
||||||
|
|
||||||
|
ensureIpServerStopped(mConfiguredBluetoothIface);
|
||||||
|
mConfiguredBluetoothIface = null;
|
||||||
|
}
|
||||||
|
|
||||||
private int setEthernetTethering(final boolean enable) {
|
private int setEthernetTethering(final boolean enable) {
|
||||||
final EthernetManager em = (EthernetManager) mContext.getSystemService(
|
final EthernetManager em = (EthernetManager) mContext.getSystemService(
|
||||||
Context.ETHERNET_SERVICE);
|
Context.ETHERNET_SERVICE);
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package com.android.networkstack.tethering;
|
||||||
|
|
||||||
import android.app.usage.NetworkStatsManager;
|
import android.app.usage.NetworkStatsManager;
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothPan;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.INetd;
|
import android.net.INetd;
|
||||||
import android.net.ip.IpServer;
|
import android.net.ip.IpServer;
|
||||||
|
|
@ -31,6 +32,8 @@ import android.text.TextUtils;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.android.internal.util.StateMachine;
|
import com.android.internal.util.StateMachine;
|
||||||
|
import com.android.networkstack.apishim.BluetoothPanShimImpl;
|
||||||
|
import com.android.networkstack.apishim.common.BluetoothPanShim;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
|
@ -158,4 +161,13 @@ public abstract class TetheringDependencies {
|
||||||
TetheringConfiguration cfg) {
|
TetheringConfiguration cfg) {
|
||||||
return new PrivateAddressCoordinator(ctx, cfg);
|
return new PrivateAddressCoordinator(ctx, cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get BluetoothPanShim object to enable/disable bluetooth tethering.
|
||||||
|
*
|
||||||
|
* TODO: use BluetoothPan directly when mainline module is built with API 32.
|
||||||
|
*/
|
||||||
|
public BluetoothPanShim getBluetoothPanShim(BluetoothPan pan) {
|
||||||
|
return BluetoothPanShimImpl.newInstance(pan);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package android.net.ip;
|
package android.net.ip;
|
||||||
|
|
||||||
|
import static android.net.INetd.IF_STATE_DOWN;
|
||||||
import static android.net.INetd.IF_STATE_UP;
|
import static android.net.INetd.IF_STATE_UP;
|
||||||
import static android.net.RouteInfo.RTN_UNICAST;
|
import static android.net.RouteInfo.RTN_UNICAST;
|
||||||
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
|
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
|
||||||
|
|
@ -33,6 +34,7 @@ import static android.net.ip.IpServer.STATE_TETHERED;
|
||||||
import static android.net.ip.IpServer.STATE_UNAVAILABLE;
|
import static android.net.ip.IpServer.STATE_UNAVAILABLE;
|
||||||
import static android.system.OsConstants.ETH_P_IPV6;
|
import static android.system.OsConstants.ETH_P_IPV6;
|
||||||
|
|
||||||
|
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
|
||||||
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.netlink.NetlinkConstants.RTM_DELNEIGH;
|
import static com.android.net.module.util.netlink.NetlinkConstants.RTM_DELNEIGH;
|
||||||
import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWNEIGH;
|
import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWNEIGH;
|
||||||
|
|
@ -400,11 +402,16 @@ public class IpServerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void canBeTethered() throws Exception {
|
public void canBeTetheredAsBluetooth() throws Exception {
|
||||||
initStateMachine(TETHERING_BLUETOOTH);
|
initStateMachine(TETHERING_BLUETOOTH);
|
||||||
|
|
||||||
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
|
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
|
||||||
InOrder inOrder = inOrder(mCallback, mNetd);
|
InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
|
||||||
|
if (isAtLeastT()) {
|
||||||
|
inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
|
||||||
|
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg ->
|
||||||
|
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);
|
inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||||
// One for ipv4 route, one for ipv6 link local route.
|
// One for ipv4 route, one for ipv6 link local route.
|
||||||
|
|
@ -426,7 +433,13 @@ public class IpServerTest {
|
||||||
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);
|
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||||
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
|
// 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.
|
||||||
|
if (isAtLeastT()) {
|
||||||
|
inOrder.verify(mNetd).interfaceSetCfg(
|
||||||
|
argThat(cfg -> assertContainsFlag(cfg.flags, IF_STATE_DOWN)));
|
||||||
|
}
|
||||||
|
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> cfg.flags.length == 0));
|
||||||
inOrder.verify(mAddressCoordinator).releaseDownstream(any());
|
inOrder.verify(mAddressCoordinator).releaseDownstream(any());
|
||||||
inOrder.verify(mCallback).updateInterfaceState(
|
inOrder.verify(mCallback).updateInterfaceState(
|
||||||
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
|
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
|
||||||
|
|
@ -443,7 +456,7 @@ public class IpServerTest {
|
||||||
InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
|
InOrder inOrder = inOrder(mCallback, mNetd, mAddressCoordinator);
|
||||||
inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
|
inOrder.verify(mAddressCoordinator).requestDownstreamAddress(any(), eq(true));
|
||||||
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);
|
inOrder.verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||||
inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
|
inOrder.verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(IFACE_NAME),
|
||||||
|
|
@ -587,7 +600,8 @@ public class IpServerTest {
|
||||||
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);
|
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
|
||||||
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
|
inOrder.verify(mNetd, times(isAtLeastT() ? 2 : 1)).interfaceSetCfg(
|
||||||
|
argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
|
||||||
inOrder.verify(mAddressCoordinator).releaseDownstream(any());
|
inOrder.verify(mAddressCoordinator).releaseDownstream(any());
|
||||||
inOrder.verify(mBpfCoordinator).tetherOffloadClientClear(mIpServer);
|
inOrder.verify(mBpfCoordinator).tetherOffloadClientClear(mIpServer);
|
||||||
inOrder.verify(mBpfCoordinator).stopMonitoring(mIpServer);
|
inOrder.verify(mBpfCoordinator).stopMonitoring(mIpServer);
|
||||||
|
|
@ -683,7 +697,11 @@ public class IpServerTest {
|
||||||
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
|
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
|
||||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
|
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
|
||||||
|
|
||||||
assertDhcpStarted(mBluetoothPrefix);
|
if (isAtLeastT()) {
|
||||||
|
assertDhcpStarted(PrefixUtils.asIpPrefix(mTestAddress));
|
||||||
|
} else {
|
||||||
|
assertDhcpStarted(mBluetoothPrefix);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -1371,7 +1389,6 @@ public class IpServerTest {
|
||||||
for (String flag : flags) {
|
for (String flag : flags) {
|
||||||
if (flag.equals(match)) return true;
|
if (flag.equals(match)) return true;
|
||||||
}
|
}
|
||||||
fail("Missing flag: " + match);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
|
||||||
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
|
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.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.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
|
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
|
||||||
|
|
@ -81,6 +82,8 @@ import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.junit.Assume.assumeFalse;
|
||||||
|
import static org.junit.Assume.assumeTrue;
|
||||||
import static org.mockito.ArgumentMatchers.argThat;
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
import static org.mockito.ArgumentMatchers.notNull;
|
import static org.mockito.ArgumentMatchers.notNull;
|
||||||
import static org.mockito.Matchers.anyInt;
|
import static org.mockito.Matchers.anyInt;
|
||||||
|
|
@ -182,6 +185,10 @@ import com.android.internal.util.StateMachine;
|
||||||
import com.android.internal.util.test.BroadcastInterceptingContext;
|
import com.android.internal.util.test.BroadcastInterceptingContext;
|
||||||
import com.android.internal.util.test.FakeSettingsProvider;
|
import com.android.internal.util.test.FakeSettingsProvider;
|
||||||
import com.android.net.module.util.CollectionUtils;
|
import com.android.net.module.util.CollectionUtils;
|
||||||
|
import com.android.networkstack.apishim.common.BluetoothPanShim;
|
||||||
|
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceCallbackShim;
|
||||||
|
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceRequestShim;
|
||||||
|
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
|
||||||
import com.android.networkstack.tethering.TestConnectivityManager.TestNetworkAgent;
|
import com.android.networkstack.tethering.TestConnectivityManager.TestNetworkAgent;
|
||||||
import com.android.testutils.MiscAsserts;
|
import com.android.testutils.MiscAsserts;
|
||||||
|
|
||||||
|
|
@ -261,6 +268,8 @@ public class TetheringTest {
|
||||||
@Mock private PackageManager mPackageManager;
|
@Mock private PackageManager mPackageManager;
|
||||||
@Mock private BluetoothAdapter mBluetoothAdapter;
|
@Mock private BluetoothAdapter mBluetoothAdapter;
|
||||||
@Mock private BluetoothPan mBluetoothPan;
|
@Mock private BluetoothPan mBluetoothPan;
|
||||||
|
@Mock private BluetoothPanShim mBluetoothPanShim;
|
||||||
|
@Mock private TetheredInterfaceRequestShim mTetheredInterfaceRequestShim;
|
||||||
|
|
||||||
private final MockIpServerDependencies mIpServerDependencies =
|
private final MockIpServerDependencies mIpServerDependencies =
|
||||||
spy(new MockIpServerDependencies());
|
spy(new MockIpServerDependencies());
|
||||||
|
|
@ -285,6 +294,7 @@ public class TetheringTest {
|
||||||
private PrivateAddressCoordinator mPrivateAddressCoordinator;
|
private PrivateAddressCoordinator mPrivateAddressCoordinator;
|
||||||
private SoftApCallback mSoftApCallback;
|
private SoftApCallback mSoftApCallback;
|
||||||
private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
|
private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
|
||||||
|
private TetheredInterfaceCallbackShim mTetheredInterfaceCallbackShim;
|
||||||
|
|
||||||
private TestConnectivityManager mCm;
|
private TestConnectivityManager mCm;
|
||||||
|
|
||||||
|
|
@ -483,13 +493,23 @@ public class TetheringTest {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
|
public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
|
||||||
TetheringConfiguration cfg) {
|
TetheringConfiguration cfg) {
|
||||||
mPrivateAddressCoordinator = super.getPrivateAddressCoordinator(ctx, cfg);
|
mPrivateAddressCoordinator = super.getPrivateAddressCoordinator(ctx, cfg);
|
||||||
return mPrivateAddressCoordinator;
|
return mPrivateAddressCoordinator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BluetoothPanShim getBluetoothPanShim(BluetoothPan pan) {
|
||||||
|
try {
|
||||||
|
when(mBluetoothPanShim.requestTetheredInterface(
|
||||||
|
any(), any())).thenReturn(mTetheredInterfaceRequestShim);
|
||||||
|
} catch (UnsupportedApiLevelException e) {
|
||||||
|
fail("BluetoothPan#requestTetheredInterface is not supported");
|
||||||
|
}
|
||||||
|
return mBluetoothPanShim;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LinkProperties buildUpstreamLinkProperties(String interfaceName,
|
private static LinkProperties buildUpstreamLinkProperties(String interfaceName,
|
||||||
|
|
@ -2557,6 +2577,44 @@ public class TetheringTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBluetoothTethering() throws Exception {
|
public void testBluetoothTethering() throws Exception {
|
||||||
|
// Switch to @IgnoreUpTo(Build.VERSION_CODES.S_V2) when it is available for AOSP.
|
||||||
|
assumeTrue(isAtLeastT());
|
||||||
|
|
||||||
|
final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
|
||||||
|
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
|
||||||
|
mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
verifySetBluetoothTethering(true /* enable */, true /* bindToPanService */);
|
||||||
|
result.assertHasResult();
|
||||||
|
|
||||||
|
mTetheredInterfaceCallbackShim.onAvailable(TEST_BT_IFNAME);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
verifyNetdCommandForBtSetup();
|
||||||
|
|
||||||
|
// If PAN disconnect, tethering should also be stopped.
|
||||||
|
mTetheredInterfaceCallbackShim.onUnavailable();
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
verifyNetdCommandForBtTearDown();
|
||||||
|
|
||||||
|
// Tethering could restart if PAN reconnect.
|
||||||
|
mTetheredInterfaceCallbackShim.onAvailable(TEST_BT_IFNAME);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
verifyNetdCommandForBtSetup();
|
||||||
|
|
||||||
|
// Pretend that bluetooth tethering was disabled.
|
||||||
|
mockBluetoothSettings(true /* bluetoothOn */, false /* tetheringOn */);
|
||||||
|
mTethering.stopTethering(TETHERING_BLUETOOTH);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
verifySetBluetoothTethering(false /* enable */, false /* bindToPanService */);
|
||||||
|
|
||||||
|
verifyNetdCommandForBtTearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBluetoothTetheringBeforeT() throws Exception {
|
||||||
|
// Switch to @IgnoreAfter(Build.VERSION_CODES.S_V2) when it is available for AOSP.
|
||||||
|
assumeFalse(isAtLeastT());
|
||||||
|
|
||||||
final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
|
final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
|
||||||
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
|
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
|
||||||
mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
|
mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
|
||||||
|
|
@ -2610,12 +2668,17 @@ public class TetheringTest {
|
||||||
mTethering.interfaceAdded(TEST_BT_IFNAME);
|
mTethering.interfaceAdded(TEST_BT_IFNAME);
|
||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
|
|
||||||
mTethering.interfaceStatusChanged(TEST_BT_IFNAME, false);
|
if (isAtLeastT()) {
|
||||||
mTethering.interfaceStatusChanged(TEST_BT_IFNAME, true);
|
mTetheredInterfaceCallbackShim.onAvailable(TEST_BT_IFNAME);
|
||||||
final ResultListener tetherResult = new ResultListener(TETHER_ERROR_NO_ERROR);
|
mLooper.dispatchAll();
|
||||||
mTethering.tether(TEST_BT_IFNAME, IpServer.STATE_TETHERED, tetherResult);
|
} else {
|
||||||
mLooper.dispatchAll();
|
mTethering.interfaceStatusChanged(TEST_BT_IFNAME, false);
|
||||||
tetherResult.assertHasResult();
|
mTethering.interfaceStatusChanged(TEST_BT_IFNAME, true);
|
||||||
|
final ResultListener tetherResult = new ResultListener(TETHER_ERROR_NO_ERROR);
|
||||||
|
mTethering.tether(TEST_BT_IFNAME, IpServer.STATE_TETHERED, tetherResult);
|
||||||
|
mLooper.dispatchAll();
|
||||||
|
tetherResult.assertHasResult();
|
||||||
|
}
|
||||||
|
|
||||||
verifyNetdCommandForBtSetup();
|
verifyNetdCommandForBtSetup();
|
||||||
|
|
||||||
|
|
@ -2632,6 +2695,10 @@ public class TetheringTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyNetdCommandForBtSetup() throws Exception {
|
private void verifyNetdCommandForBtSetup() throws Exception {
|
||||||
|
if (isAtLeastT()) {
|
||||||
|
verify(mNetd).interfaceSetCfg(argThat(cfg -> TEST_BT_IFNAME.equals(cfg.ifName)
|
||||||
|
&& 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);
|
verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
|
||||||
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_BT_IFNAME),
|
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_BT_IFNAME),
|
||||||
|
|
@ -2644,19 +2711,30 @@ public class TetheringTest {
|
||||||
reset(mNetd);
|
reset(mNetd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean assertContainsFlag(String[] flags, String match) {
|
||||||
|
for (String flag : flags) {
|
||||||
|
if (flag.equals(match)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
|
||||||
verify(mNetd).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
|
// 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.
|
||||||
|
verify(mNetd, times(isAtLeastT() ? 2 : 1)).interfaceSetCfg(
|
||||||
|
any(InterfaceConfigurationParcel.class));
|
||||||
verify(mNetd).tetherStop();
|
verify(mNetd).tetherStop();
|
||||||
verify(mNetd).ipfwdDisableForwarding(TETHERING_NAME);
|
verify(mNetd).ipfwdDisableForwarding(TETHERING_NAME);
|
||||||
|
reset(mNetd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If bindToPanService is true, this function would return ServiceListener which could notify
|
// If bindToPanService is true, this function would return ServiceListener which could notify
|
||||||
// PanService is connected or disconnected.
|
// PanService is connected or disconnected.
|
||||||
private ServiceListener verifySetBluetoothTethering(final boolean enable,
|
private ServiceListener verifySetBluetoothTethering(final boolean enable,
|
||||||
final boolean bindToPanService) {
|
final boolean bindToPanService) throws Exception {
|
||||||
ServiceListener listener = null;
|
ServiceListener listener = null;
|
||||||
verify(mBluetoothAdapter).isEnabled();
|
verify(mBluetoothAdapter).isEnabled();
|
||||||
if (bindToPanService) {
|
if (bindToPanService) {
|
||||||
|
|
@ -2671,7 +2749,19 @@ public class TetheringTest {
|
||||||
verify(mBluetoothAdapter, never()).getProfileProxy(eq(mServiceContext), any(),
|
verify(mBluetoothAdapter, never()).getProfileProxy(eq(mServiceContext), any(),
|
||||||
anyInt());
|
anyInt());
|
||||||
}
|
}
|
||||||
verify(mBluetoothPan).setBluetoothTethering(enable);
|
|
||||||
|
if (isAtLeastT()) {
|
||||||
|
if (enable) {
|
||||||
|
final ArgumentCaptor<TetheredInterfaceCallbackShim> callbackCaptor =
|
||||||
|
ArgumentCaptor.forClass(TetheredInterfaceCallbackShim.class);
|
||||||
|
verify(mBluetoothPanShim).requestTetheredInterface(any(), callbackCaptor.capture());
|
||||||
|
mTetheredInterfaceCallbackShim = callbackCaptor.getValue();
|
||||||
|
} else {
|
||||||
|
verify(mTetheredInterfaceRequestShim).release();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
verify(mBluetoothPan).setBluetoothTethering(enable);
|
||||||
|
}
|
||||||
verify(mBluetoothPan).isTetheringOn();
|
verify(mBluetoothPan).isTetheringOn();
|
||||||
verifyNoMoreInteractions(mBluetoothAdapter, mBluetoothPan);
|
verifyNoMoreInteractions(mBluetoothAdapter, mBluetoothPan);
|
||||||
reset(mBluetoothAdapter, mBluetoothPan);
|
reset(mBluetoothAdapter, mBluetoothPan);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue