From 37a22754c877efebd3a5b14a119c712ea88c26ab Mon Sep 17 00:00:00 2001 From: Automerger Merge Worker Date: Tue, 17 Mar 2020 16:59:57 +0000 Subject: [PATCH] Support static address configuration Application can specify static ipv4 server and client address to setup tethering and this is one shot configuration. Tethering service would not save the configuration and the configuration would be reset when tethering stop or start failure. When startTethering callback fired, it just mean tethering is requested successful. Therefore, callers may call startTethering again if startTethering successful but do not receive following tethering active notification for a while. Tethering service never actually does anything synchronously when startTethering is called: -startProvisioningIfNeeded just posts a message to the handler thread. -enableTetheringInternal doesn't do anything synchronously, it just asks the downstreams to get their interfaces ready and waits for callbacks. If tethering is already enabled with a different request, tethering would be disabled and re-enabled. Bug: 141256482 Test: -build, flash, boot -atest TetheringTests -atest CtsTetheringTest Change-Id: I2b2dd965a673e6f1626738d41b5d443f0f9fbd0e Merged-In: I0399917e7cefa1547d617e688225544c4fc1a231 (cherry picked from commit 5d6723e24e21154bef3967585a8adc069e007f49) --- api/module-lib-current.txt | 4 +- api/system-current.txt | 4 +- api/test-current.txt | 4 +- .../TetheringLib/api/module-lib-current.txt | 4 +- .../TetheringLib/api/system-current.txt | 4 +- .../src/android/net/TetheringManager.java | 36 +++++- .../android/net/TetheringRequestParcel.aidl | 1 + .../src/android/net/ip/IpServer.java | 28 ++++- .../src/android/net/util/TetheringUtils.java | 16 +++ .../connectivity/tethering/Tethering.java | 73 ++++++++---- .../android/net/util/TetheringUtilsTest.java | 87 ++++++++++++++ .../connectivity/tethering/TetheringTest.java | 107 ++++++++++++++++-- 12 files changed, 325 insertions(+), 43 deletions(-) create mode 100644 packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index ee997f1542992..c3c31f5b81c0a 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -127,9 +127,11 @@ package android.net { public static class TetheringManager.TetheringRequest.Builder { ctor public TetheringManager.TetheringRequest.Builder(int); method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); + method @Nullable public android.net.LinkAddress getLocalIpv4Address(); method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); - method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress); + method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); } } diff --git a/api/system-current.txt b/api/system-current.txt index fd3798908d1ea..ae48477d60e68 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -6597,9 +6597,11 @@ package android.net { public static class TetheringManager.TetheringRequest.Builder { ctor public TetheringManager.TetheringRequest.Builder(int); method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); + method @Nullable public android.net.LinkAddress getLocalIpv4Address(); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); } public class TrafficStats { diff --git a/api/test-current.txt b/api/test-current.txt index 66d6af9b67c78..c83eed823828f 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1975,9 +1975,11 @@ package android.net { public static class TetheringManager.TetheringRequest.Builder { ctor public TetheringManager.TetheringRequest.Builder(int); method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); + method @Nullable public android.net.LinkAddress getLocalIpv4Address(); method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); - method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress); + method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); } public class TrafficStats { diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt index e25d77dfc9a81..8a7e975051a58 100644 --- a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt +++ b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt @@ -117,9 +117,11 @@ package android.net { public static class TetheringManager.TetheringRequest.Builder { ctor public TetheringManager.TetheringRequest.Builder(int); method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); + method @Nullable public android.net.LinkAddress getLocalIpv4Address(); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); } } diff --git a/packages/Tethering/common/TetheringLib/api/system-current.txt b/packages/Tethering/common/TetheringLib/api/system-current.txt index d6fcb625bdae9..ac739532a4909 100644 --- a/packages/Tethering/common/TetheringLib/api/system-current.txt +++ b/packages/Tethering/common/TetheringLib/api/system-current.txt @@ -95,9 +95,11 @@ package android.net { public static class TetheringManager.TetheringRequest.Builder { ctor public TetheringManager.TetheringRequest.Builder(int); method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); + method @Nullable public android.net.LinkAddress getLocalIpv4Address(); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); } } diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index 7f831ced7beca..f2045df947e4b 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -512,19 +512,36 @@ public class TetheringManager { mBuilderParcel = new TetheringRequestParcel(); mBuilderParcel.tetheringType = type; mBuilderParcel.localIPv4Address = null; + mBuilderParcel.staticClientAddress = null; mBuilderParcel.exemptFromEntitlementCheck = false; 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) @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.staticClientAddress = clientAddress; return this; } @@ -549,6 +566,18 @@ public class TetheringManager { public TetheringRequest build() { 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() { return "TetheringRequest [ type= " + mRequestParcel.tetheringType + ", localIPv4Address= " + mRequestParcel.localIPv4Address + + ", staticClientAddress= " + mRequestParcel.staticClientAddress + ", exemptFromEntitlementCheck= " + mRequestParcel.exemptFromEntitlementCheck + ", showProvisioningUi= " + mRequestParcel.showProvisioningUi + " ]"; diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl index bf19d85f6a836..c0280d3dbfaf1 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl @@ -25,6 +25,7 @@ import android.net.LinkAddress; parcelable TetheringRequestParcel { int tetheringType; LinkAddress localIPv4Address; + LinkAddress staticClientAddress; boolean exemptFromEntitlementCheck; boolean showProvisioningUi; } diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java index 6c0c432d46dcd..433b903ebe521 100644 --- a/packages/Tethering/src/android/net/ip/IpServer.java +++ b/packages/Tethering/src/android/net/ip/IpServer.java @@ -35,6 +35,7 @@ import android.net.MacAddress; import android.net.RouteInfo; import android.net.TetheredClient; import android.net.TetheringManager; +import android.net.TetheringRequestParcel; import android.net.dhcp.DhcpLeaseParcelable; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; @@ -243,6 +244,10 @@ public class IpServer extends StateMachine { private IDhcpServer mDhcpServer; private RaParams mLastRaParams; private LinkAddress mIpv4Address; + + private LinkAddress mStaticIpv4ServerAddr; + private LinkAddress mStaticIpv4ClientAddr; + @NonNull private List mDhcpLeases = Collections.emptyList(); @@ -547,6 +552,8 @@ public class IpServer extends StateMachine { // into calls to InterfaceController, shared with startIPv4(). mInterfaceCtrl.clearIPv4Address(); mIpv4Address = null; + mStaticIpv4ServerAddr = null; + mStaticIpv4ClientAddr = null; } private boolean configureIPv4(boolean enabled) { @@ -557,7 +564,10 @@ public class IpServer extends StateMachine { final Inet4Address srvAddr; int prefixLen = 0; 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) { srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR); prefixLen = USB_PREFIX_LENGTH; @@ -602,10 +612,6 @@ public class IpServer extends StateMachine { return false; } - if (!configureDhcp(enabled, srvAddr, prefixLen)) { - return false; - } - // Directly-connected route. final IpPrefix ipv4Prefix = new IpPrefix(mIpv4Address.getAddress(), mIpv4Address.getPrefixLength()); @@ -617,7 +623,8 @@ public class IpServer extends StateMachine { mLinkProperties.removeLinkAddress(mIpv4Address); mLinkProperties.removeRoute(route); } - return true; + + return configureDhcp(enabled, srvAddr, prefixLen); } private String getRandomWifiIPv4Address() { @@ -937,6 +944,13 @@ public class IpServer extends StateMachine { mLinkProperties.setInterfaceName(mIfaceName); } + private void maybeConfigureStaticIp(final TetheringRequestParcel request) { + if (request == null) return; + + mStaticIpv4ServerAddr = request.localIPv4Address; + mStaticIpv4ClientAddr = request.staticClientAddress; + } + class InitialState extends State { @Override public void enter() { @@ -951,9 +965,11 @@ public class IpServer extends StateMachine { mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; switch (message.arg1) { case STATE_LOCAL_ONLY: + maybeConfigureStaticIp((TetheringRequestParcel) message.obj); transitionTo(mLocalHotspotState); break; case STATE_TETHERED: + maybeConfigureStaticIp((TetheringRequestParcel) message.obj); transitionTo(mTetheredState); break; default: diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java index 5a6d5c1cbfb00..dd67dddae1cd7 100644 --- a/packages/Tethering/src/android/net/util/TetheringUtils.java +++ b/packages/Tethering/src/android/net/util/TetheringUtils.java @@ -15,8 +15,11 @@ */ package android.net.util; +import android.net.TetheringRequestParcel; + import java.io.FileDescriptor; import java.net.SocketException; +import java.util.Objects; /** * Native methods for tethering utilization. @@ -38,4 +41,17 @@ public class TetheringUtils { public static int uint16(short s) { 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; + } } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 3d8dbab7d4fb6..6e918929f87ea 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -92,6 +92,7 @@ import android.net.util.BaseNetdUnsolicitedEventListener; import android.net.util.InterfaceSet; import android.net.util.PrefixUtils; import android.net.util.SharedLog; +import android.net.util.TetheringUtils; import android.net.util.VersionedBroadcastListener; import android.net.wifi.WifiClient; import android.net.wifi.WifiManager; @@ -196,6 +197,11 @@ public class Tethering { private final SharedLog mLog = new SharedLog(TAG); private final RemoteCallbackList mTetheringEventCallbacks = 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 mActiveTetheringRequests = + new SparseArray<>(); // used to synchronize public access to members private final Object mPublicSync; @@ -487,14 +493,31 @@ public class Tethering { } void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) { - mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType, - request.showProvisioningUi); - enableTetheringInternal(request.tetheringType, true /* enabled */, 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, + request.showProvisioningUi); + enableTetheringInternal(request.tetheringType, true /* enabled */, listener); + }); } void stopTethering(int type) { - enableTetheringInternal(type, false /* disabled */, null); - mEntitlementMgr.stopProvisioningIfNeeded(type); + mHandler.post(() -> { + 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, final IIntResultListener listener) { - int result; + int result = TETHER_ERROR_NO_ERROR; switch (type) { case TETHERING_WIFI: result = setWifiTethering(enable); - sendTetherResult(listener, result); break; case TETHERING_USB: result = setUsbTethering(enable); - sendTetherResult(listener, result); break; case TETHERING_BLUETOOTH: setBluetoothTethering(enable, listener); break; case TETHERING_NCM: result = setNcmTethering(enable); - sendTetherResult(listener, result); break; case TETHERING_ETHERNET: result = setEthernetTethering(enable); - sendTetherResult(listener, result); break; default: 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) { try { listener.onResult(result); } 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) { @@ -565,7 +594,7 @@ public class Tethering { if (adapter == null || !adapter.isEnabled()) { Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " + (adapter == null)); - sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL); + sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL, TETHERING_BLUETOOTH); return; } @@ -594,7 +623,7 @@ public class Tethering { final int result = (((BluetoothPan) proxy).isTetheringOn() == enable) ? TETHER_ERROR_NO_ERROR : TETHER_ERROR_MASTER_ERROR; - sendTetherResult(listener, result); + sendTetherResult(listener, result, TETHERING_BLUETOOTH); adapter.closeProfileProxy(BluetoothProfile.PAN, proxy); } }, BluetoothProfile.PAN); @@ -672,12 +701,18 @@ public class Tethering { Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring"); return TETHER_ERROR_UNAVAIL_IFACE; } - // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's - // queue but not yet processed, this will be a no-op and it will not - // return an error. + // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's queue but not yet + // processed, this will be a no-op and it will not return an error. // - // TODO: reexamine the threading and messaging model. - tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState); + // This code cannot race with untether() because they both synchronize on mPublicSync. + // 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; } } diff --git a/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java b/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java new file mode 100644 index 0000000000000..1499f3be224e9 --- /dev/null +++ b/packages/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java @@ -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); + } +} diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 820c852a271a8..60d7ad1c5b1e1 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -82,6 +82,7 @@ import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; import android.net.EthernetManager; import android.net.EthernetManager.TetheredInterfaceRequest; +import android.net.IIntResultListener; import android.net.INetd; import android.net.ITetheringEventCallback; import android.net.InetAddresses; @@ -499,10 +500,16 @@ public class TetheringTest { 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(); request.tetheringType = type; - request.localIPv4Address = null; + request.localIPv4Address = serverAddr; + request.staticClientAddress = clientAddr; request.exemptFromEntitlementCheck = false; request.showProvisioningUi = false; @@ -616,7 +623,7 @@ public class TetheringTest { private void prepareNcmTethering() { // Emulate startTethering(TETHERING_NCM) called - mTethering.startTethering(createTetheringRquestParcel(TETHERING_NCM), null); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), null); mLooper.dispatchAll(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM); @@ -629,7 +636,7 @@ public class TetheringTest { .thenReturn(upstreamState); // Emulate pressing the USB tethering button in Settings UI. - mTethering.startTethering(createTetheringRquestParcel(TETHERING_USB), null); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), null); mLooper.dispatchAll(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); @@ -903,7 +910,7 @@ public class TetheringTest { when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); // Emulate pressing the WiFi tethering button. - mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); @@ -931,7 +938,7 @@ public class TetheringTest { when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true); // Emulate pressing the WiFi tethering button. - mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); @@ -1008,7 +1015,7 @@ public class TetheringTest { doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME); // Emulate pressing the WiFi tethering button. - mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startTetheredHotspot(null); verifyNoMoreInteractions(mWifiManager); @@ -1303,7 +1310,7 @@ public class TetheringTest { tetherState = callback.pollTetherStatesChanged(); 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); mLooper.dispatchAll(); tetherState = callback.pollTetherStatesChanged(); @@ -1398,10 +1405,10 @@ public class TetheringTest { public void testNoDuplicatedEthernetRequest() throws Exception { final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class); when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest); - mTethering.startTethering(createTetheringRquestParcel(TETHERING_ETHERNET), null); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null); mLooper.dispatchAll(); verify(mEm, times(1)).requestTetheredInterface(any(), any()); - mTethering.startTethering(createTetheringRquestParcel(TETHERING_ETHERNET), null); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null); mLooper.dispatchAll(); verifyNoMoreInteractions(mEm); mTethering.stopTethering(TETHERING_ETHERNET); @@ -1580,6 +1587,86 @@ public class TetheringTest { 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 // already operating tethering mode interface. }