Merge "Support static address configuration" am: 6d175120a0 am: 7ea8df5599 am: f465942e66

Change-Id: I663332a12188521917e9e286cd6cb8927f7efd9c
This commit is contained in:
Automerger Merge Worker
2020-03-17 14:15:06 +00:00
7 changed files with 310 additions and 38 deletions

View File

@@ -512,19 +512,36 @@ public class TetheringManager {
mBuilderParcel = new TetheringRequestParcel(); mBuilderParcel = new TetheringRequestParcel();
mBuilderParcel.tetheringType = type; mBuilderParcel.tetheringType = type;
mBuilderParcel.localIPv4Address = null; mBuilderParcel.localIPv4Address = null;
mBuilderParcel.staticClientAddress = null;
mBuilderParcel.exemptFromEntitlementCheck = false; mBuilderParcel.exemptFromEntitlementCheck = false;
mBuilderParcel.showProvisioningUi = true; mBuilderParcel.showProvisioningUi = true;
} }
/** /**
* Configure tethering with static IPv4 assignment (with DHCP disabled). * Configure tethering with static IPv4 assignment.
* *
* @param localIPv4Address The preferred local IPv4 address to use. * The clientAddress must be in the localIPv4Address prefix. A DHCP server will be
* started, but will only be able to offer the client address. The two addresses must
* be in the same prefix.
*
* @param localIPv4Address The preferred local IPv4 link address to use.
* @param clientAddress The static client address.
*/ */
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
@NonNull @NonNull
public Builder useStaticIpv4Addresses(@NonNull final LinkAddress localIPv4Address) { public Builder setStaticIpv4Addresses(@NonNull final LinkAddress localIPv4Address,
@NonNull final LinkAddress clientAddress) {
Objects.requireNonNull(localIPv4Address);
Objects.requireNonNull(clientAddress);
if (localIPv4Address.getPrefixLength() != clientAddress.getPrefixLength()
|| !localIPv4Address.isIpv4() || !clientAddress.isIpv4()
|| !new IpPrefix(localIPv4Address.toString()).equals(
new IpPrefix(clientAddress.toString()))) {
throw new IllegalArgumentException("Invalid server or client addresses");
}
mBuilderParcel.localIPv4Address = localIPv4Address; mBuilderParcel.localIPv4Address = localIPv4Address;
mBuilderParcel.staticClientAddress = clientAddress;
return this; return this;
} }
@@ -549,6 +566,18 @@ public class TetheringManager {
public TetheringRequest build() { public TetheringRequest build() {
return new TetheringRequest(mBuilderParcel); return new TetheringRequest(mBuilderParcel);
} }
/** Get static server address. */
@Nullable
public LinkAddress getLocalIpv4Address() {
return mBuilderParcel.localIPv4Address;
}
/** Get static client address. */
@Nullable
public LinkAddress getClientStaticIpv4Address() {
return mBuilderParcel.staticClientAddress;
}
} }
/** /**
@@ -563,6 +592,7 @@ public class TetheringManager {
public String toString() { public String toString() {
return "TetheringRequest [ type= " + mRequestParcel.tetheringType return "TetheringRequest [ type= " + mRequestParcel.tetheringType
+ ", localIPv4Address= " + mRequestParcel.localIPv4Address + ", localIPv4Address= " + mRequestParcel.localIPv4Address
+ ", staticClientAddress= " + mRequestParcel.staticClientAddress
+ ", exemptFromEntitlementCheck= " + ", exemptFromEntitlementCheck= "
+ mRequestParcel.exemptFromEntitlementCheck + ", showProvisioningUi= " + mRequestParcel.exemptFromEntitlementCheck + ", showProvisioningUi= "
+ mRequestParcel.showProvisioningUi + " ]"; + mRequestParcel.showProvisioningUi + " ]";

View File

@@ -25,6 +25,7 @@ import android.net.LinkAddress;
parcelable TetheringRequestParcel { parcelable TetheringRequestParcel {
int tetheringType; int tetheringType;
LinkAddress localIPv4Address; LinkAddress localIPv4Address;
LinkAddress staticClientAddress;
boolean exemptFromEntitlementCheck; boolean exemptFromEntitlementCheck;
boolean showProvisioningUi; boolean showProvisioningUi;
} }

View File

@@ -35,6 +35,7 @@ import android.net.MacAddress;
import android.net.RouteInfo; import android.net.RouteInfo;
import android.net.TetheredClient; import android.net.TetheredClient;
import android.net.TetheringManager; import android.net.TetheringManager;
import android.net.TetheringRequestParcel;
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;
@@ -243,6 +244,10 @@ public class IpServer extends StateMachine {
private IDhcpServer mDhcpServer; private IDhcpServer mDhcpServer;
private RaParams mLastRaParams; private RaParams mLastRaParams;
private LinkAddress mIpv4Address; private LinkAddress mIpv4Address;
private LinkAddress mStaticIpv4ServerAddr;
private LinkAddress mStaticIpv4ClientAddr;
@NonNull @NonNull
private List<TetheredClient> mDhcpLeases = Collections.emptyList(); private List<TetheredClient> mDhcpLeases = Collections.emptyList();
@@ -547,6 +552,8 @@ public class IpServer extends StateMachine {
// into calls to InterfaceController, shared with startIPv4(). // into calls to InterfaceController, shared with startIPv4().
mInterfaceCtrl.clearIPv4Address(); mInterfaceCtrl.clearIPv4Address();
mIpv4Address = null; mIpv4Address = null;
mStaticIpv4ServerAddr = null;
mStaticIpv4ClientAddr = null;
} }
private boolean configureIPv4(boolean enabled) { private boolean configureIPv4(boolean enabled) {
@@ -557,7 +564,10 @@ public class IpServer extends StateMachine {
final Inet4Address srvAddr; final Inet4Address srvAddr;
int prefixLen = 0; int prefixLen = 0;
try { try {
if (mInterfaceType == TetheringManager.TETHERING_USB if (mStaticIpv4ServerAddr != null) {
srvAddr = (Inet4Address) mStaticIpv4ServerAddr.getAddress();
prefixLen = mStaticIpv4ServerAddr.getPrefixLength();
} else if (mInterfaceType == TetheringManager.TETHERING_USB
|| mInterfaceType == TetheringManager.TETHERING_NCM) { || mInterfaceType == TetheringManager.TETHERING_NCM) {
srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR); srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR);
prefixLen = USB_PREFIX_LENGTH; prefixLen = USB_PREFIX_LENGTH;
@@ -602,10 +612,6 @@ public class IpServer extends StateMachine {
return false; return false;
} }
if (!configureDhcp(enabled, srvAddr, prefixLen)) {
return false;
}
// Directly-connected route. // Directly-connected route.
final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(), final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(),
mIpv4Address.getPrefixLength()); mIpv4Address.getPrefixLength());
@@ -617,7 +623,8 @@ public class IpServer extends StateMachine {
mLinkProperties.removeLinkAddress(mIpv4Address); mLinkProperties.removeLinkAddress(mIpv4Address);
mLinkProperties.removeRoute(route); mLinkProperties.removeRoute(route);
} }
return true;
return configureDhcp(enabled, srvAddr, prefixLen);
} }
private String getRandomWifiIPv4Address() { private String getRandomWifiIPv4Address() {
@@ -937,6 +944,13 @@ public class IpServer extends StateMachine {
mLinkProperties.setInterfaceName(mIfaceName); mLinkProperties.setInterfaceName(mIfaceName);
} }
private void maybeConfigureStaticIp(final TetheringRequestParcel request) {
if (request == null) return;
mStaticIpv4ServerAddr = request.localIPv4Address;
mStaticIpv4ClientAddr = request.staticClientAddress;
}
class InitialState extends State { class InitialState extends State {
@Override @Override
public void enter() { public void enter() {
@@ -951,9 +965,11 @@ public class IpServer extends StateMachine {
mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
switch (message.arg1) { switch (message.arg1) {
case STATE_LOCAL_ONLY: case STATE_LOCAL_ONLY:
maybeConfigureStaticIp((TetheringRequestParcel) message.obj);
transitionTo(mLocalHotspotState); transitionTo(mLocalHotspotState);
break; break;
case STATE_TETHERED: case STATE_TETHERED:
maybeConfigureStaticIp((TetheringRequestParcel) message.obj);
transitionTo(mTetheredState); transitionTo(mTetheredState);
break; break;
default: default:

View File

@@ -15,8 +15,11 @@
*/ */
package android.net.util; package android.net.util;
import android.net.TetheringRequestParcel;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.net.SocketException; import java.net.SocketException;
import java.util.Objects;
/** /**
* Native methods for tethering utilization. * Native methods for tethering utilization.
@@ -38,4 +41,17 @@ public class TetheringUtils {
public static int uint16(short s) { public static int uint16(short s) {
return s & 0xffff; return s & 0xffff;
} }
/** Check whether two TetheringRequestParcels are the same. */
public static boolean isTetheringRequestEquals(final TetheringRequestParcel request,
final TetheringRequestParcel otherRequest) {
if (request == otherRequest) return true;
return request != null && otherRequest != null
&& request.tetheringType == otherRequest.tetheringType
&& Objects.equals(request.localIPv4Address, otherRequest.localIPv4Address)
&& Objects.equals(request.staticClientAddress, otherRequest.staticClientAddress)
&& request.exemptFromEntitlementCheck == otherRequest.exemptFromEntitlementCheck
&& request.showProvisioningUi == otherRequest.showProvisioningUi;
}
} }

View File

@@ -92,6 +92,7 @@ import android.net.util.BaseNetdUnsolicitedEventListener;
import android.net.util.InterfaceSet; import android.net.util.InterfaceSet;
import android.net.util.PrefixUtils; import android.net.util.PrefixUtils;
import android.net.util.SharedLog; import android.net.util.SharedLog;
import android.net.util.TetheringUtils;
import android.net.util.VersionedBroadcastListener; import android.net.util.VersionedBroadcastListener;
import android.net.wifi.WifiClient; import android.net.wifi.WifiClient;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
@@ -196,6 +197,11 @@ public class Tethering {
private final SharedLog mLog = new SharedLog(TAG); private final SharedLog mLog = new SharedLog(TAG);
private final RemoteCallbackList<ITetheringEventCallback> mTetheringEventCallbacks = private final RemoteCallbackList<ITetheringEventCallback> mTetheringEventCallbacks =
new RemoteCallbackList<>(); new RemoteCallbackList<>();
// Currently active tethering requests per tethering type. Only one of each type can be
// requested at a time. After a tethering type is requested, the map keeps tethering parameters
// to be used after the interface comes up asynchronously.
private final SparseArray<TetheringRequestParcel> mActiveTetheringRequests =
new SparseArray<>();
// used to synchronize public access to members // used to synchronize public access to members
private final Object mPublicSync; private final Object mPublicSync;
@@ -487,14 +493,31 @@ public class Tethering {
} }
void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) { void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) {
mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType, mHandler.post(() -> {
request.showProvisioningUi); final TetheringRequestParcel unfinishedRequest = mActiveTetheringRequests.get(
enableTetheringInternal(request.tetheringType, true /* enabled */, listener); request.tetheringType);
// If tethering is already enabled with a different request,
// disable before re-enabling.
if (unfinishedRequest != null
&& !TetheringUtils.isTetheringRequestEquals(unfinishedRequest, request)) {
enableTetheringInternal(request.tetheringType, false /* disabled */, null);
mEntitlementMgr.stopProvisioningIfNeeded(request.tetheringType);
}
mActiveTetheringRequests.put(request.tetheringType, request);
mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType,
request.showProvisioningUi);
enableTetheringInternal(request.tetheringType, true /* enabled */, listener);
});
} }
void stopTethering(int type) { void stopTethering(int type) {
enableTetheringInternal(type, false /* disabled */, null); mHandler.post(() -> {
mEntitlementMgr.stopProvisioningIfNeeded(type); mActiveTetheringRequests.remove(type);
enableTetheringInternal(type, false /* disabled */, null);
mEntitlementMgr.stopProvisioningIfNeeded(type);
});
} }
/** /**
@@ -503,39 +526,45 @@ public class Tethering {
*/ */
private void enableTetheringInternal(int type, boolean enable, private void enableTetheringInternal(int type, boolean enable,
final IIntResultListener listener) { final IIntResultListener listener) {
int result; int result = TETHER_ERROR_NO_ERROR;
switch (type) { switch (type) {
case TETHERING_WIFI: case TETHERING_WIFI:
result = setWifiTethering(enable); result = setWifiTethering(enable);
sendTetherResult(listener, result);
break; break;
case TETHERING_USB: case TETHERING_USB:
result = setUsbTethering(enable); result = setUsbTethering(enable);
sendTetherResult(listener, result);
break; break;
case TETHERING_BLUETOOTH: case TETHERING_BLUETOOTH:
setBluetoothTethering(enable, listener); setBluetoothTethering(enable, listener);
break; break;
case TETHERING_NCM: case TETHERING_NCM:
result = setNcmTethering(enable); result = setNcmTethering(enable);
sendTetherResult(listener, result);
break; break;
case TETHERING_ETHERNET: case TETHERING_ETHERNET:
result = setEthernetTethering(enable); result = setEthernetTethering(enable);
sendTetherResult(listener, result);
break; break;
default: default:
Log.w(TAG, "Invalid tether type."); Log.w(TAG, "Invalid tether type.");
sendTetherResult(listener, TETHER_ERROR_UNKNOWN_IFACE); result = TETHER_ERROR_UNKNOWN_IFACE;
}
// The result of Bluetooth tethering will be sent by #setBluetoothTethering.
if (type != TETHERING_BLUETOOTH) {
sendTetherResult(listener, result, type);
} }
} }
private void sendTetherResult(final IIntResultListener listener, int result) { private void sendTetherResult(final IIntResultListener listener, final int result,
final int type) {
if (listener != null) { if (listener != null) {
try { try {
listener.onResult(result); listener.onResult(result);
} catch (RemoteException e) { } } catch (RemoteException e) { }
} }
// If changing tethering fail, remove corresponding request
// no matter who trigger the start/stop.
if (result != TETHER_ERROR_NO_ERROR) mActiveTetheringRequests.remove(type);
} }
private int setWifiTethering(final boolean enable) { private int setWifiTethering(final boolean enable) {
@@ -565,7 +594,7 @@ public class Tethering {
if (adapter == null || !adapter.isEnabled()) { if (adapter == null || !adapter.isEnabled()) {
Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: "
+ (adapter == null)); + (adapter == null));
sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL); sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL, TETHERING_BLUETOOTH);
return; return;
} }
@@ -594,7 +623,7 @@ public class Tethering {
final int result = (((BluetoothPan) proxy).isTetheringOn() == enable) final int result = (((BluetoothPan) proxy).isTetheringOn() == enable)
? TETHER_ERROR_NO_ERROR ? TETHER_ERROR_NO_ERROR
: TETHER_ERROR_MASTER_ERROR; : TETHER_ERROR_MASTER_ERROR;
sendTetherResult(listener, result); sendTetherResult(listener, result, TETHERING_BLUETOOTH);
adapter.closeProfileProxy(BluetoothProfile.PAN, proxy); adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
} }
}, BluetoothProfile.PAN); }, BluetoothProfile.PAN);
@@ -672,12 +701,18 @@ public class Tethering {
Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring"); Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
return TETHER_ERROR_UNAVAIL_IFACE; return TETHER_ERROR_UNAVAIL_IFACE;
} }
// NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's queue but not yet
// queue but not yet processed, this will be a no-op and it will not // processed, this will be a no-op and it will not return an error.
// return an error.
// //
// TODO: reexamine the threading and messaging model. // This code cannot race with untether() because they both synchronize on mPublicSync.
tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState); // TODO: reexamine the threading and messaging model to totally remove mPublicSync.
final int type = tetherState.ipServer.interfaceType();
final TetheringRequestParcel request = mActiveTetheringRequests.get(type, null);
if (request != null) {
mActiveTetheringRequests.delete(type);
}
tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState, 0,
request);
return TETHER_ERROR_NO_ERROR; return TETHER_ERROR_NO_ERROR;
} }
} }

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net.util;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import android.net.LinkAddress;
import android.net.TetheringRequestParcel;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.testutils.MiscAssertsKt;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class TetheringUtilsTest {
private static final LinkAddress TEST_SERVER_ADDR = new LinkAddress("192.168.43.1/24");
private static final LinkAddress TEST_CLIENT_ADDR = new LinkAddress("192.168.43.5/24");
private TetheringRequestParcel mTetheringRequest;
@Before
public void setUp() {
mTetheringRequest = makeTetheringRequestParcel();
}
public TetheringRequestParcel makeTetheringRequestParcel() {
final TetheringRequestParcel request = new TetheringRequestParcel();
request.tetheringType = TETHERING_WIFI;
request.localIPv4Address = TEST_SERVER_ADDR;
request.staticClientAddress = TEST_CLIENT_ADDR;
request.exemptFromEntitlementCheck = false;
request.showProvisioningUi = true;
return request;
}
@Test
public void testIsTetheringRequestEquals() throws Exception {
TetheringRequestParcel request = makeTetheringRequestParcel();
assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, mTetheringRequest));
assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
assertTrue(TetheringUtils.isTetheringRequestEquals(null, null));
assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, null));
assertFalse(TetheringUtils.isTetheringRequestEquals(null, mTetheringRequest));
request = makeTetheringRequestParcel();
request.tetheringType = TETHERING_USB;
assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
request = makeTetheringRequestParcel();
request.localIPv4Address = null;
request.staticClientAddress = null;
assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
request = makeTetheringRequestParcel();
request.exemptFromEntitlementCheck = true;
assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
request = makeTetheringRequestParcel();
request.showProvisioningUi = false;
assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request));
MiscAssertsKt.assertFieldCountEquals(5, TetheringRequestParcel.class);
}
}

View File

@@ -82,6 +82,7 @@ import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.EthernetManager; import android.net.EthernetManager;
import android.net.EthernetManager.TetheredInterfaceRequest; import android.net.EthernetManager.TetheredInterfaceRequest;
import android.net.IIntResultListener;
import android.net.INetd; import android.net.INetd;
import android.net.ITetheringEventCallback; import android.net.ITetheringEventCallback;
import android.net.InetAddresses; import android.net.InetAddresses;
@@ -499,10 +500,16 @@ public class TetheringTest {
return new Tethering(mTetheringDependencies); return new Tethering(mTetheringDependencies);
} }
private TetheringRequestParcel createTetheringRquestParcel(final int type) { private TetheringRequestParcel createTetheringRequestParcel(final int type) {
return createTetheringRequestParcel(type, null, null);
}
private TetheringRequestParcel createTetheringRequestParcel(final int type,
final LinkAddress serverAddr, final LinkAddress clientAddr) {
final TetheringRequestParcel request = new TetheringRequestParcel(); final TetheringRequestParcel request = new TetheringRequestParcel();
request.tetheringType = type; request.tetheringType = type;
request.localIPv4Address = null; request.localIPv4Address = serverAddr;
request.staticClientAddress = clientAddr;
request.exemptFromEntitlementCheck = false; request.exemptFromEntitlementCheck = false;
request.showProvisioningUi = false; request.showProvisioningUi = false;
@@ -616,7 +623,7 @@ public class TetheringTest {
private void prepareNcmTethering() { private void prepareNcmTethering() {
// Emulate startTethering(TETHERING_NCM) called // Emulate startTethering(TETHERING_NCM) called
mTethering.startTethering(createTetheringRquestParcel(TETHERING_NCM), null); mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), null);
mLooper.dispatchAll(); mLooper.dispatchAll();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM);
@@ -629,7 +636,7 @@ public class TetheringTest {
.thenReturn(upstreamState); .thenReturn(upstreamState);
// Emulate pressing the USB tethering button in Settings UI. // Emulate pressing the USB tethering button in Settings UI.
mTethering.startTethering(createTetheringRquestParcel(TETHERING_USB), null); mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), null);
mLooper.dispatchAll(); mLooper.dispatchAll();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
@@ -903,7 +910,7 @@ public class TetheringTest {
when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button. // Emulate pressing the WiFi tethering button.
mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
mLooper.dispatchAll(); mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null); verify(mWifiManager, times(1)).startTetheredHotspot(null);
verifyNoMoreInteractions(mWifiManager); verifyNoMoreInteractions(mWifiManager);
@@ -931,7 +938,7 @@ public class TetheringTest {
when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button. // Emulate pressing the WiFi tethering button.
mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
mLooper.dispatchAll(); mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null); verify(mWifiManager, times(1)).startTetheredHotspot(null);
verifyNoMoreInteractions(mWifiManager); verifyNoMoreInteractions(mWifiManager);
@@ -1008,7 +1015,7 @@ public class TetheringTest {
doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME); doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME);
// Emulate pressing the WiFi tethering button. // Emulate pressing the WiFi tethering button.
mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
mLooper.dispatchAll(); mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null); verify(mWifiManager, times(1)).startTetheredHotspot(null);
verifyNoMoreInteractions(mWifiManager); verifyNoMoreInteractions(mWifiManager);
@@ -1303,7 +1310,7 @@ public class TetheringTest {
tetherState = callback.pollTetherStatesChanged(); tetherState = callback.pollTetherStatesChanged();
assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME}); assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME});
mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
mLooper.dispatchAll(); mLooper.dispatchAll();
tetherState = callback.pollTetherStatesChanged(); tetherState = callback.pollTetherStatesChanged();
@@ -1398,10 +1405,10 @@ public class TetheringTest {
public void testNoDuplicatedEthernetRequest() throws Exception { public void testNoDuplicatedEthernetRequest() throws Exception {
final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class); final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class);
when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest); when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
mTethering.startTethering(createTetheringRquestParcel(TETHERING_ETHERNET), null); mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
mLooper.dispatchAll(); mLooper.dispatchAll();
verify(mEm, times(1)).requestTetheredInterface(any(), any()); verify(mEm, times(1)).requestTetheredInterface(any(), any());
mTethering.startTethering(createTetheringRquestParcel(TETHERING_ETHERNET), null); mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
mLooper.dispatchAll(); mLooper.dispatchAll();
verifyNoMoreInteractions(mEm); verifyNoMoreInteractions(mEm);
mTethering.stopTethering(TETHERING_ETHERNET); mTethering.stopTethering(TETHERING_ETHERNET);
@@ -1580,6 +1587,86 @@ public class TetheringTest {
assertTrue(element + " not found in " + collection, collection.contains(element)); assertTrue(element + " not found in " + collection, collection.contains(element));
} }
private class ResultListener extends IIntResultListener.Stub {
private final int mExpectedResult;
private boolean mHasResult = false;
ResultListener(final int expectedResult) {
mExpectedResult = expectedResult;
}
@Override
public void onResult(final int resultCode) {
mHasResult = true;
if (resultCode != mExpectedResult) {
fail("expected result: " + mExpectedResult + " but actual result: " + resultCode);
}
}
public void assertHasResult() {
if (!mHasResult) fail("No callback result");
}
}
@Test
public void testMultipleStartTethering() throws Exception {
final LinkAddress serverLinkAddr = new LinkAddress("192.168.20.1/24");
final LinkAddress clientLinkAddr = new LinkAddress("192.168.20.42/24");
final String serverAddr = "192.168.20.1";
final ResultListener firstResult = new ResultListener(TETHER_ERROR_NO_ERROR);
final ResultListener secondResult = new ResultListener(TETHER_ERROR_NO_ERROR);
final ResultListener thirdResult = new ResultListener(TETHER_ERROR_NO_ERROR);
// Enable USB tethering and check that Tethering starts USB.
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
null, null), firstResult);
mLooper.dispatchAll();
firstResult.assertHasResult();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
verifyNoMoreInteractions(mUsbManager);
// Enable USB tethering again with the same request and expect no change to USB.
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
null, null), secondResult);
mLooper.dispatchAll();
secondResult.assertHasResult();
verify(mUsbManager, never()).setCurrentFunctions(UsbManager.FUNCTION_NONE);
reset(mUsbManager);
// Enable USB tethering with a different request and expect that USB is stopped and
// started.
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
serverLinkAddr, clientLinkAddr), thirdResult);
mLooper.dispatchAll();
thirdResult.assertHasResult();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
// Expect that when USB comes up, the DHCP server is configured with the requested address.
mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
sendUsbBroadcast(true, true, true, TETHERING_USB);
mLooper.dispatchAll();
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr)));
}
@Test
public void testRequestStaticServerIp() throws Exception {
final LinkAddress serverLinkAddr = new LinkAddress("192.168.20.1/24");
final LinkAddress clientLinkAddr = new LinkAddress("192.168.20.42/24");
final String serverAddr = "192.168.20.1";
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
serverLinkAddr, clientLinkAddr), null);
mLooper.dispatchAll();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
sendUsbBroadcast(true, true, true, TETHERING_USB);
mLooper.dispatchAll();
verify(mNetd).interfaceSetCfg(argThat(cfg -> serverAddr.equals(cfg.ipv4Addr)));
// TODO: test static client address.
}
// TODO: Test that a request for hotspot mode doesn't interfere with an // TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface. // already operating tethering mode interface.
} }