Merge "Support static address configuration" am: 6d175120a0
Change-Id: I9637c44f3e88e5e9d08be59a0eb03e019e9919e1
This commit is contained in:
@@ -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 + " ]";
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
mHandler.post(() -> {
|
||||||
|
final TetheringRequestParcel unfinishedRequest = mActiveTetheringRequests.get(
|
||||||
|
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,
|
mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType,
|
||||||
request.showProvisioningUi);
|
request.showProvisioningUi);
|
||||||
enableTetheringInternal(request.tetheringType, true /* enabled */, listener);
|
enableTetheringInternal(request.tetheringType, true /* enabled */, listener);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void stopTethering(int type) {
|
void stopTethering(int type) {
|
||||||
|
mHandler.post(() -> {
|
||||||
|
mActiveTetheringRequests.remove(type);
|
||||||
|
|
||||||
enableTetheringInternal(type, false /* disabled */, null);
|
enableTetheringInternal(type, false /* disabled */, null);
|
||||||
mEntitlementMgr.stopProvisioningIfNeeded(type);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
|
when(mWifiManager.startSoftAp(any(WifiConfiguration.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)).startSoftAp(null);
|
verify(mWifiManager, times(1)).startSoftAp(null);
|
||||||
verifyNoMoreInteractions(mWifiManager);
|
verifyNoMoreInteractions(mWifiManager);
|
||||||
@@ -931,7 +938,7 @@ public class TetheringTest {
|
|||||||
when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
|
when(mWifiManager.startSoftAp(any(WifiConfiguration.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)).startSoftAp(null);
|
verify(mWifiManager, times(1)).startSoftAp(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)).startSoftAp(null);
|
verify(mWifiManager, times(1)).startSoftAp(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.
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user