From eab1de47f4c71719fe2cb689ef5c6edf5e05ae28 Mon Sep 17 00:00:00 2001 From: Seungjae Yoo Date: Tue, 10 Sep 2024 12:11:34 +0900 Subject: [PATCH] Introduce interfaceName field into TetheringRequestParcel Bug: 340376953 API-Coverage-Bug: 365678805 Test: Presubmit Test: atest EthernetTetheringTest Test: atest TetheringTest Test: atest NsdManagerDownstreamTetheringTest -b Test: atest TetheringManagerTest -b Change-Id: I8025cc2e238b8f9516bf18d8ea0253f1c58a05e6 --- .../TetheringLib/api/module-lib-current.txt | 5 ++ .../src/android/net/TetheringManager.java | 51 ++++++++++++++++++- .../android/net/TetheringRequestParcel.aidl | 1 + .../networkstack/tethering/Tethering.java | 22 ++++---- .../tethering/TetheringService.java | 11 +++- .../net/EthernetTetheringTestBase.java | 33 ++++++------ .../android/net/EthernetTetheringTest.java | 47 ++++++++++++++--- .../tethering/TetheringServiceTest.java | 32 ++++++++++++ .../networkstack/tethering/TetheringTest.java | 39 ++++++++++---- .../cts/NsdManagerDownstreamTetheringTest.kt | 4 +- .../tethering/cts/TetheringManagerTest.java | 37 +++++++++++--- 11 files changed, 229 insertions(+), 53 deletions(-) diff --git a/Tethering/common/TetheringLib/api/module-lib-current.txt b/Tethering/common/TetheringLib/api/module-lib-current.txt index e8938940c9..01bd98363b 100644 --- a/Tethering/common/TetheringLib/api/module-lib-current.txt +++ b/Tethering/common/TetheringLib/api/module-lib-current.txt @@ -47,9 +47,14 @@ package android.net { } public static final class TetheringManager.TetheringRequest implements android.os.Parcelable { + method @FlaggedApi("com.android.net.flags.tethering_with_soft_ap_config") @Nullable public String getInterfaceName(); method @FlaggedApi("com.android.net.flags.tethering_with_soft_ap_config") @Nullable public String getPackageName(); method @FlaggedApi("com.android.net.flags.tethering_with_soft_ap_config") public int getUid(); } + public static class TetheringManager.TetheringRequest.Builder { + method @FlaggedApi("com.android.net.flags.tethering_with_soft_ap_config") @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public android.net.TetheringManager.TetheringRequest.Builder setInterfaceName(@Nullable String); + } + } diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index bc771da4ef..6d6eb82f60 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -136,6 +136,7 @@ public class TetheringManager { TETHERING_WIFI_P2P, TETHERING_NCM, TETHERING_ETHERNET, + TETHERING_VIRTUAL, }) public @interface TetheringType { } @@ -253,6 +254,7 @@ public class TetheringManager { @Retention(RetentionPolicy.SOURCE) @IntDef(value = { TETHER_ERROR_SERVICE_UNAVAIL, + TETHER_ERROR_UNSUPPORTED, TETHER_ERROR_INTERNAL_ERROR, TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION, TETHER_ERROR_UNKNOWN_TYPE, @@ -775,6 +777,7 @@ public class TetheringManager { mBuilderParcel.connectivityScope = getDefaultConnectivityScope(type); mBuilderParcel.uid = Process.INVALID_UID; mBuilderParcel.softApConfig = null; + mBuilderParcel.interfaceName = null; } /** @@ -820,6 +823,35 @@ public class TetheringManager { return this; } + /** + * Sets the name of the interface. Currently supported only for + * - {@link #TETHERING_VIRTUAL}. + * - {@link #TETHERING_WIFI} (for Local-only Hotspot) + * - {@link #TETHERING_WIFI_P2P} + * @hide + */ + @FlaggedApi(Flags.FLAG_TETHERING_WITH_SOFT_AP_CONFIG) + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_STACK, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, + }) + @NonNull + @SystemApi(client = MODULE_LIBRARIES) + public Builder setInterfaceName(@Nullable final String interfaceName) { + switch (mBuilderParcel.tetheringType) { + case TETHERING_VIRTUAL: + case TETHERING_WIFI_P2P: + case TETHERING_WIFI: + break; + default: + throw new IllegalArgumentException("Interface name cannot be set for" + + " tethering type " + interfaceName); + } + mBuilderParcel.interfaceName = interfaceName; + return this; + } + /** * Sets the connectivity scope to be provided by this tethering downstream. */ @@ -905,6 +937,17 @@ public class TetheringManager { return mRequestParcel.showProvisioningUi; } + /** + * Get interface name. + * @hide + */ + @FlaggedApi(Flags.FLAG_TETHERING_WITH_SOFT_AP_CONFIG) + @Nullable + @SystemApi(client = MODULE_LIBRARIES) + public String getInterfaceName() { + return mRequestParcel.interfaceName; + } + /** * Check whether the two addresses are ipv4 and in the same prefix. * @hide @@ -1022,6 +1065,9 @@ public class TetheringManager { if (mRequestParcel.packageName != null) { sj.add("packageName=" + mRequestParcel.packageName); } + if (mRequestParcel.interfaceName != null) { + sj.add("interfaceName=" + mRequestParcel.interfaceName); + } return sj.toString(); } @@ -1039,7 +1085,8 @@ public class TetheringManager { && parcel.connectivityScope == otherParcel.connectivityScope && Objects.equals(parcel.softApConfig, otherParcel.softApConfig) && parcel.uid == otherParcel.uid - && Objects.equals(parcel.packageName, otherParcel.packageName); + && Objects.equals(parcel.packageName, otherParcel.packageName) + && Objects.equals(parcel.interfaceName, otherParcel.interfaceName); } @Override @@ -1048,7 +1095,7 @@ public class TetheringManager { return Objects.hash(parcel.tetheringType, parcel.localIPv4Address, parcel.staticClientAddress, parcel.exemptFromEntitlementCheck, parcel.showProvisioningUi, parcel.connectivityScope, parcel.softApConfig, - parcel.uid, parcel.packageName); + parcel.uid, parcel.packageName, parcel.interfaceName); } } diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl b/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl index 789d5bb9ce..97c9b9a604 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl +++ b/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl @@ -33,4 +33,5 @@ parcelable TetheringRequestParcel { SoftApConfiguration softApConfig; int uid; String packageName; + String interfaceName; } diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index f33ef37025..254b60fda3 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -665,7 +665,8 @@ public class Tethering { // If tethering is already enabled with a different request, // disable before re-enabling. if (unfinishedRequest != null && !unfinishedRequest.equals(request)) { - enableTetheringInternal(type, false /* disabled */, null); + enableTetheringInternal(type, false /* disabled */, + unfinishedRequest.getInterfaceName(), null); mEntitlementMgr.stopProvisioningIfNeeded(type); } mActiveTetheringRequests.put(type, request); @@ -676,7 +677,7 @@ public class Tethering { mEntitlementMgr.startProvisioningIfNeeded(type, request.getShouldShowEntitlementUi()); } - enableTetheringInternal(type, true /* enabled */, listener); + enableTetheringInternal(type, true /* enabled */, request.getInterfaceName(), listener); mTetheringMetrics.createBuilder(type, callerPkg); }); } @@ -689,7 +690,7 @@ public class Tethering { void stopTetheringInternal(int type) { mActiveTetheringRequests.remove(type); - enableTetheringInternal(type, false /* disabled */, null); + enableTetheringInternal(type, false /* disabled */, null, null); mEntitlementMgr.stopProvisioningIfNeeded(type); } @@ -698,7 +699,7 @@ public class Tethering { * schedule provisioning rechecks for the specified interface. */ private void enableTetheringInternal(int type, boolean enable, - final IIntResultListener listener) { + String iface, final IIntResultListener listener) { int result = TETHER_ERROR_NO_ERROR; switch (type) { case TETHERING_WIFI: @@ -717,7 +718,7 @@ public class Tethering { result = setEthernetTethering(enable); break; case TETHERING_VIRTUAL: - result = setVirtualMachineTethering(enable); + result = setVirtualMachineTethering(enable, iface); break; default: Log.w(TAG, "Invalid tether type."); @@ -972,10 +973,13 @@ public class Tethering { } } - private int setVirtualMachineTethering(final boolean enable) { - // TODO(340377643): Use bridge ifname when it's introduced, not fixed TAP ifname. + private int setVirtualMachineTethering(final boolean enable, String iface) { if (enable) { - mConfiguredVirtualIface = "avf_tap_fixed"; + if (TextUtils.isEmpty(iface)) { + mConfiguredVirtualIface = "avf_tap_fixed"; + } else { + mConfiguredVirtualIface = iface; + } enableIpServing( TETHERING_VIRTUAL, mConfiguredVirtualIface, @@ -2205,7 +2209,7 @@ public class Tethering { case EVENT_REQUEST_CHANGE_DOWNSTREAM: { final boolean enabled = message.arg1 == 1; final TetheringRequest request = (TetheringRequest) message.obj; - enableTetheringInternal(request.getTetheringType(), enabled, null); + enableTetheringInternal(request.getTetheringType(), enabled, null, null); break; } default: diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java index 3cb5f99d13..6485ffd643 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java @@ -133,9 +133,11 @@ public class TetheringService extends Service { @Override public void startTethering(TetheringRequestParcel request, String callerPkg, String callingAttributionTag, IIntResultListener listener) { + boolean onlyAllowPrivileged = request.exemptFromEntitlementCheck + || request.interfaceName != null; if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, - request.exemptFromEntitlementCheck /* onlyAllowPrivileged */, + onlyAllowPrivileged, listener)) { return; } @@ -284,6 +286,10 @@ public class TetheringService extends Service { return false; } + private boolean hasNetworkSettingsPermission() { + return checkCallingOrSelfPermission(NETWORK_SETTINGS); + } + private boolean hasNetworkStackPermission() { return checkCallingOrSelfPermission(NETWORK_STACK) || checkCallingOrSelfPermission(PERMISSION_MAINLINE_NETWORK_STACK); @@ -299,7 +305,8 @@ public class TetheringService extends Service { private boolean hasTetherChangePermission(final String callerPkg, final String callingAttributionTag, final boolean onlyAllowPrivileged) { - if (onlyAllowPrivileged && !hasNetworkStackPermission()) return false; + if (onlyAllowPrivileged && !hasNetworkStackPermission() + && !hasNetworkSettingsPermission()) return false; if (hasTetherPrivilegedPermission()) return true; diff --git a/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java b/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java index 01f3af9ba4..1323f28ff7 100644 --- a/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java +++ b/Tethering/tests/integration/base/android/net/EthernetTetheringTestBase.java @@ -332,15 +332,16 @@ public abstract class EthernetTetheringTestBase { // seconds. See b/289881008. private static final int EXPANDED_TIMEOUT_MS = 30000; - MyTetheringEventCallback(String iface) { - mIface = new TetheringInterface(TETHERING_ETHERNET, iface); + MyTetheringEventCallback(int tetheringType, String iface) { + mIface = new TetheringInterface(tetheringType, iface); mExpectedUpstream = null; mAcceptAnyUpstream = true; } - MyTetheringEventCallback(String iface, @NonNull Network expectedUpstream) { + MyTetheringEventCallback( + int tetheringType, String iface, @NonNull Network expectedUpstream) { Objects.requireNonNull(expectedUpstream); - mIface = new TetheringInterface(TETHERING_ETHERNET, iface); + mIface = new TetheringInterface(tetheringType, iface); mExpectedUpstream = expectedUpstream; mAcceptAnyUpstream = false; } @@ -392,12 +393,12 @@ public abstract class EthernetTetheringTestBase { } public void awaitInterfaceTethered() throws Exception { - assertTrue("Ethernet not tethered after " + EXPANDED_TIMEOUT_MS + "ms", + assertTrue("Interface is not tethered after " + EXPANDED_TIMEOUT_MS + "ms", mTetheringStartedLatch.await(EXPANDED_TIMEOUT_MS, TimeUnit.MILLISECONDS)); } public void awaitInterfaceLocalOnly() throws Exception { - assertTrue("Ethernet not local-only after " + EXPANDED_TIMEOUT_MS + "ms", + assertTrue("Interface is not local-only after " + EXPANDED_TIMEOUT_MS + "ms", mLocalOnlyStartedLatch.await(EXPANDED_TIMEOUT_MS, TimeUnit.MILLISECONDS)); } @@ -501,15 +502,17 @@ public abstract class EthernetTetheringTestBase { sCallbackErrors.add(error); } - protected static MyTetheringEventCallback enableEthernetTethering(String iface, + protected static MyTetheringEventCallback enableTethering(String iface, TetheringRequest request, Network expectedUpstream) throws Exception { - // Enable ethernet tethering with null expectedUpstream means the test accept any upstream - // after etherent tethering started. + // Enable tethering with null expectedUpstream means the test accept any upstream after + // tethering started. final MyTetheringEventCallback callback; if (expectedUpstream != null) { - callback = new MyTetheringEventCallback(iface, expectedUpstream); + callback = + new MyTetheringEventCallback( + request.getTetheringType(), iface, expectedUpstream); } else { - callback = new MyTetheringEventCallback(iface); + callback = new MyTetheringEventCallback(request.getTetheringType(), iface); } runAsShell(NETWORK_SETTINGS, () -> { sTm.registerTetheringEventCallback(c -> c.run() /* executor */, callback); @@ -521,7 +524,7 @@ public abstract class EthernetTetheringTestBase { StartTetheringCallback startTetheringCallback = new StartTetheringCallback() { @Override public void onTetheringStarted() { - Log.d(TAG, "Ethernet tethering started"); + Log.d(TAG, "Tethering started"); tetheringStartedLatch.countDown(); } @@ -530,8 +533,8 @@ public abstract class EthernetTetheringTestBase { addCallbackError("Unexpectedly got onTetheringFailed"); } }; - Log.d(TAG, "Starting Ethernet tethering"); - runAsShell(TETHER_PRIVILEGED, () -> { + Log.d(TAG, "Starting tethering"); + runAsShell(TETHER_PRIVILEGED, NETWORK_SETTINGS, () -> { sTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback); // Binder call is an async call. Need to hold the shell permission until tethering // started. This helps to avoid the test become flaky. @@ -557,7 +560,7 @@ public abstract class EthernetTetheringTestBase { protected static MyTetheringEventCallback enableEthernetTethering(String iface, Network expectedUpstream) throws Exception { - return enableEthernetTethering(iface, + return enableTethering(iface, new TetheringRequest.Builder(TETHERING_ETHERNET) .setShouldShowEntitlementUi(false).build(), expectedUpstream); } diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index 1bbea940e1..5c8d347164 100644 --- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -19,7 +19,9 @@ package android.net; import static android.Manifest.permission.DUMP; import static android.net.InetAddresses.parseNumericAddress; import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL; +import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL; import static android.net.TetheringManager.TETHERING_ETHERNET; +import static android.net.TetheringManager.TETHERING_VIRTUAL; import static android.net.TetheringTester.TestDnsPacket; import static android.net.TetheringTester.buildIcmpEchoPacketV4; import static android.net.TetheringTester.buildUdpPacket; @@ -236,7 +238,8 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase { downstreamReader = makePacketReader(fd, mtu); tetheringEventCallback = enableEthernetTethering(downstreamIface.getInterfaceName(), null /* any upstream */); - checkTetheredClientCallbacks(downstreamReader, tetheringEventCallback); + checkTetheredClientCallbacks( + downstreamReader, TETHERING_ETHERNET, tetheringEventCallback); } finally { maybeStopTapPacketReader(downstreamReader); maybeCloseTestInterface(downstreamIface); @@ -267,7 +270,8 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase { downstreamReader = makePacketReader(fd, getMTU(downstreamIface)); tetheringEventCallback = enableEthernetTethering(downstreamIface.getInterfaceName(), null /* any upstream */); - checkTetheredClientCallbacks(downstreamReader, tetheringEventCallback); + checkTetheredClientCallbacks( + downstreamReader, TETHERING_ETHERNET, tetheringEventCallback); } finally { maybeStopTapPacketReader(downstreamReader); maybeCloseTestInterface(downstreamIface); @@ -302,7 +306,7 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase { final String localAddr = "192.0.2.3/28"; final String clientAddr = "192.0.2.2/28"; - tetheringEventCallback = enableEthernetTethering(iface, + tetheringEventCallback = enableTethering(iface, requestWithStaticIpv4(localAddr, clientAddr), null /* any upstream */); tetheringEventCallback.awaitInterfaceTethered(); @@ -368,8 +372,7 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase { final TetheringRequest request = new TetheringRequest.Builder(TETHERING_ETHERNET) .setConnectivityScope(CONNECTIVITY_SCOPE_LOCAL).build(); - tetheringEventCallback = enableEthernetTethering(iface, request, - null /* any upstream */); + tetheringEventCallback = enableTethering(iface, request, null /* any upstream */); tetheringEventCallback.awaitInterfaceLocalOnly(); // makePacketReader only works after tethering is started, because until then the @@ -424,6 +427,7 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase { } private void checkTetheredClientCallbacks(final PollPacketReader packetReader, + final int tetheringType, final MyTetheringEventCallback tetheringEventCallback) throws Exception { // Create a fake client. byte[] clientMacAddr = new byte[6]; @@ -438,7 +442,7 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase { // Check the MAC address. assertEquals(MacAddress.fromBytes(clientMacAddr), client.getMacAddress()); - assertEquals(TETHERING_ETHERNET, client.getTetheringType()); + assertEquals(tetheringType, client.getTetheringType()); // Check the hostname. assertEquals(1, client.getAddresses().size()); @@ -475,8 +479,7 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase { private void assertInvalidStaticIpv4Request(String iface, String local, String client) throws Exception { try { - enableEthernetTethering(iface, requestWithStaticIpv4(local, client), - null /* any upstream */); + enableTethering(iface, requestWithStaticIpv4(local, client), null /* any upstream */); fail("Unexpectedly accepted invalid IPv4 configuration: " + local + ", " + client); } catch (IllegalArgumentException | NullPointerException expected) { } } @@ -1180,4 +1183,32 @@ public class EthernetTetheringTest extends EthernetTetheringTestBase { TX_UDP_PACKET_COUNT * (TX_UDP_PACKET_SIZE + IPV6_HEADER_LEN - IPV4_HEADER_MIN_LEN), newEgress4.bytes - oldEgress4.bytes); } + + @Test + public void testTetheringVirtual() throws Exception { + assumeFalse(isInterfaceForTetheringAvailable()); + setIncludeTestInterfaces(true); + + TestNetworkInterface downstreamIface = null; + MyTetheringEventCallback tetheringEventCallback = null; + PollPacketReader downstreamReader = null; + try { + downstreamIface = createTestInterface(); + String iface = downstreamIface.getInterfaceName(); + final TetheringRequest request = new TetheringRequest.Builder(TETHERING_VIRTUAL) + .setConnectivityScope(CONNECTIVITY_SCOPE_GLOBAL) + .setInterfaceName(iface) + .build(); + tetheringEventCallback = enableTethering(iface, request, null /* any upstream */); + + FileDescriptor fd = downstreamIface.getFileDescriptor().getFileDescriptor(); + downstreamReader = makePacketReader(fd, getMTU(downstreamIface)); + checkTetheredClientCallbacks( + downstreamReader, TETHERING_VIRTUAL, tetheringEventCallback); + } finally { + maybeStopTapPacketReader(downstreamReader); + maybeCloseTestInterface(downstreamIface); + maybeUnregisterTetheringEventCallback(tetheringEventCallback); + } + } } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java index 0dbf772e8e..d94852ef13 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java @@ -17,9 +17,11 @@ package com.android.networkstack.tethering; import static android.Manifest.permission.ACCESS_NETWORK_STATE; +import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.TETHER_PRIVILEGED; import static android.Manifest.permission.WRITE_SETTINGS; import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.net.TetheringManager.TETHERING_VIRTUAL; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION; import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION; @@ -171,6 +173,10 @@ public final class TetheringServiceTest { runTetheringCall(test, false /* isTetheringAllowed */, TETHER_PRIVILEGED); } + private void runAsNetworkSettings(final TestTetheringCall test) throws Exception { + runTetheringCall(test, true /* isTetheringAllowed */, NETWORK_SETTINGS, TETHER_PRIVILEGED); + } + private void runTetheringCall(final TestTetheringCall test, boolean isTetheringAllowed, String... permissions) throws Exception { // Allow the test to run even if ACCESS_NETWORK_STATE was granted at the APK level @@ -370,6 +376,32 @@ public final class TetheringServiceTest { }); } + @Test + public void testStartTetheringWithInterfaceSucceeds() throws Exception { + final TetheringRequestParcel request = new TetheringRequestParcel(); + request.tetheringType = TETHERING_VIRTUAL; + request.interfaceName = "avf_tap_fixed"; + + runAsNetworkSettings((result) -> { + runStartTethering(result, request); + verifyNoMoreInteractionsForTethering(); + }); + } + + @Test + public void testStartTetheringNoNetworkStackPermissionWithInterfaceFails() throws Exception { + final TetheringRequestParcel request = new TetheringRequestParcel(); + request.tetheringType = TETHERING_VIRTUAL; + request.interfaceName = "avf_tap_fixed"; + + runAsTetherPrivileged((result) -> { + mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, + result); + result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION); + verifyNoMoreInteractionsForTethering(); + }); + } + private void runStartTetheringAndVerifyNoPermission(final TestTetheringResult result) throws Exception { final TetheringRequestParcel request = new TetheringRequestParcel(); diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index 17f50814c0..0c6a95d3d1 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -47,6 +47,7 @@ import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_USB; +import static android.net.TetheringManager.TETHERING_VIRTUAL; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR; @@ -255,6 +256,7 @@ public class TetheringTest { private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0"; private static final String TEST_NCM_IFNAME = "test_ncm0"; private static final String TEST_ETH_IFNAME = "test_eth0"; + private static final String TEST_VIRT_IFNAME = "test_virt0"; private static final String TEST_BT_IFNAME = "test_pan0"; private static final String TETHERING_NAME = "Tethering"; private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; @@ -417,11 +419,12 @@ public class TetheringTest { || ifName.equals(TEST_P2P_IFNAME) || ifName.equals(TEST_NCM_IFNAME) || ifName.equals(TEST_ETH_IFNAME) - || ifName.equals(TEST_BT_IFNAME)); + || ifName.equals(TEST_BT_IFNAME) + || ifName.equals(TEST_VIRT_IFNAME)); final String[] ifaces = new String[] { TEST_RNDIS_IFNAME, TEST_WLAN_IFNAME, TEST_WLAN2_IFNAME, TEST_WIFI_IFNAME, TEST_MOBILE_IFNAME, TEST_DUN_IFNAME, TEST_P2P_IFNAME, TEST_NCM_IFNAME, - TEST_ETH_IFNAME}; + TEST_ETH_IFNAME, TEST_VIRT_IFNAME}; return new InterfaceParams(ifName, CollectionUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET, MacAddress.ALL_ZEROS_ADDRESS); @@ -674,7 +677,8 @@ public class TetheringTest { when(mNetd.interfaceGetList()) .thenReturn(new String[] { TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_WLAN2_IFNAME, TEST_RNDIS_IFNAME, - TEST_P2P_IFNAME, TEST_NCM_IFNAME, TEST_ETH_IFNAME, TEST_BT_IFNAME}); + TEST_P2P_IFNAME, TEST_NCM_IFNAME, TEST_ETH_IFNAME, TEST_BT_IFNAME, + TEST_VIRT_IFNAME}); when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn(""); mInterfaceConfiguration = new InterfaceConfigurationParcel(); mInterfaceConfiguration.flags = new String[0]; @@ -765,12 +769,12 @@ public class TetheringTest { } private TetheringRequest createTetheringRequest(final int type) { - return createTetheringRequest(type, null, null, false, CONNECTIVITY_SCOPE_GLOBAL); + return createTetheringRequest(type, null, null, false, CONNECTIVITY_SCOPE_GLOBAL, null); } private TetheringRequest createTetheringRequest(final int type, final LinkAddress localIPv4Address, final LinkAddress staticClientAddress, - final boolean exempt, final int scope) { + final boolean exempt, final int scope, final String interfaceName) { TetheringRequest.Builder builder = new TetheringRequest.Builder(type) .setExemptFromEntitlementCheck(exempt) .setConnectivityScope(scope) @@ -778,6 +782,9 @@ public class TetheringTest { if (localIPv4Address != null && staticClientAddress != null) { builder.setStaticIpv4Addresses(localIPv4Address, staticClientAddress); } + if (interfaceName != null) { + builder.setInterfaceName(interfaceName); + } return builder.build(); } @@ -2782,7 +2789,7 @@ public class TetheringTest { // Enable USB tethering with a different request and expect that USB is stopped and // started. mTethering.startTethering(createTetheringRequest(TETHERING_USB, - serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL), + serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL, null), TEST_CALLER_PKG, thirdResult); mLooper.dispatchAll(); thirdResult.assertHasResult(); @@ -2813,7 +2820,7 @@ public class TetheringTest { final ArgumentCaptor dhcpParamsCaptor = ArgumentCaptor.forClass(DhcpServingParamsParcel.class); mTethering.startTethering(createTetheringRequest(TETHERING_USB, - serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL), + serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL, null), TEST_CALLER_PKG, null); mLooper.dispatchAll(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM); @@ -2942,7 +2949,7 @@ public class TetheringTest { setupForRequiredProvisioning(); final TetheringRequest wifiNotExemptRequest = createTetheringRequest(TETHERING_WIFI, null, null, false, - CONNECTIVITY_SCOPE_GLOBAL); + CONNECTIVITY_SCOPE_GLOBAL, null); mTethering.startTethering(wifiNotExemptRequest, TEST_CALLER_PKG, null); mLooper.dispatchAll(); verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false); @@ -2956,7 +2963,7 @@ public class TetheringTest { setupForRequiredProvisioning(); final TetheringRequest wifiExemptRequest = createTetheringRequest(TETHERING_WIFI, null, null, true, - CONNECTIVITY_SCOPE_GLOBAL); + CONNECTIVITY_SCOPE_GLOBAL, null); mTethering.startTethering(wifiExemptRequest, TEST_CALLER_PKG, null); mLooper.dispatchAll(); verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false); @@ -3779,4 +3786,18 @@ public class TetheringTest { // TODO: Test that a request for hotspot mode doesn't interfere with an // already operating tethering mode interface. + + @Test + public void testVirtualTetheringWithInterfaceName() throws Exception { + initTetheringOnTestThread(); + final TetheringRequest virtualTetheringRequest = + createTetheringRequest(TETHERING_VIRTUAL, null, null, false, + CONNECTIVITY_SCOPE_GLOBAL, TEST_VIRT_IFNAME); + assertEquals(TEST_VIRT_IFNAME, virtualTetheringRequest.getInterfaceName()); + mTethering.startTethering(virtualTetheringRequest, TEST_CALLER_PKG, null); + mLooper.dispatchAll(); + assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_VIRT_IFNAME); + mTethering.stopTethering(TETHERING_VIRTUAL); + mLooper.dispatchAll(); + } } diff --git a/tests/cts/net/src/android/net/cts/NsdManagerDownstreamTetheringTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerDownstreamTetheringTest.kt index 24af42b02e..19738994c7 100644 --- a/tests/cts/net/src/android/net/cts/NsdManagerDownstreamTetheringTest.kt +++ b/tests/cts/net/src/android/net/cts/NsdManagerDownstreamTetheringTest.kt @@ -92,7 +92,7 @@ class NsdManagerDownstreamTetheringTest : EthernetTetheringTestBase() { assertEquals(downstreamIface.name, iface) val request = TetheringRequest.Builder(TETHERING_ETHERNET) .setConnectivityScope(CONNECTIVITY_SCOPE_LOCAL).build() - tetheringEventCallback = enableEthernetTethering( + tetheringEventCallback = enableTethering( iface, request, null /* any upstream */ ).apply { @@ -125,7 +125,7 @@ class NsdManagerDownstreamTetheringTest : EthernetTetheringTestBase() { val request = TetheringRequest.Builder(TETHERING_ETHERNET) .setStaticIpv4Addresses(localAddr, clientAddr) .setShouldShowEntitlementUi(false).build() - tetheringEventCallback = enableEthernetTethering( + tetheringEventCallback = enableTethering( iface, request, null /* any upstream */ ).apply { diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java index b294d63089..54ecc49bea 100644 --- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java +++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java @@ -28,6 +28,7 @@ import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_USB; +import static android.net.TetheringManager.TETHERING_VIRTUAL; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; @@ -303,6 +304,13 @@ public class TetheringManagerTest { tr3.setUid(uid); tr3.setPackageName(packageName); assertEquals(tr2, tr3); + + final String interfaceName = "test_iface"; + final TetheringRequest tr4 = new TetheringRequest.Builder(TETHERING_VIRTUAL) + .setInterfaceName(interfaceName) + .setConnectivityScope(CONNECTIVITY_SCOPE_GLOBAL).build(); + assertEquals(interfaceName, tr4.getInterfaceName()); + assertEquals(CONNECTIVITY_SCOPE_GLOBAL, tr4.getConnectivityScope()); } @Test @@ -319,24 +327,41 @@ public class TetheringManagerTest { } } + @Test + public void testTetheringRequestSetInterfaceNameFailsExceptTetheringVirtual() { + for (int type : List.of(TETHERING_USB, TETHERING_BLUETOOTH, TETHERING_NCM, + TETHERING_ETHERNET)) { + try { + new TetheringRequest.Builder(type).setInterfaceName("test_iface"); + fail("Was able to set interface name for tethering type " + type); + } catch (IllegalArgumentException e) { + // Success + } + } + } + @Test public void testTetheringRequestParcelable() { final SoftApConfiguration softApConfiguration = createSoftApConfiguration("SSID"); final LinkAddress localAddr = new LinkAddress("192.168.24.5/24"); final LinkAddress clientAddr = new LinkAddress("192.168.24.100/24"); - final TetheringRequest withConfig = new TetheringRequest.Builder(TETHERING_WIFI) + final TetheringRequest withApConfig = new TetheringRequest.Builder(TETHERING_WIFI) .setSoftApConfiguration(softApConfiguration) .setStaticIpv4Addresses(localAddr, clientAddr) .setExemptFromEntitlementCheck(true) .setShouldShowEntitlementUi(false).build(); - final TetheringRequest withoutConfig = new TetheringRequest.Builder(TETHERING_WIFI) + final TetheringRequest withoutApConfig = new TetheringRequest.Builder(TETHERING_WIFI) .setStaticIpv4Addresses(localAddr, clientAddr) .setExemptFromEntitlementCheck(true) .setShouldShowEntitlementUi(false).build(); - assertEquals(withConfig, ParcelUtils.parcelingRoundTrip(withConfig)); - assertEquals(withoutConfig, ParcelUtils.parcelingRoundTrip(withoutConfig)); - assertNotEquals(withConfig, ParcelUtils.parcelingRoundTrip(withoutConfig)); - assertNotEquals(withoutConfig, ParcelUtils.parcelingRoundTrip(withConfig)); + assertEquals(withApConfig, ParcelUtils.parcelingRoundTrip(withApConfig)); + assertEquals(withoutApConfig, ParcelUtils.parcelingRoundTrip(withoutApConfig)); + assertNotEquals(withApConfig, ParcelUtils.parcelingRoundTrip(withoutApConfig)); + assertNotEquals(withoutApConfig, ParcelUtils.parcelingRoundTrip(withApConfig)); + + final TetheringRequest withIfaceName = new TetheringRequest.Builder(TETHERING_VIRTUAL) + .setInterfaceName("test_iface").build(); + assertEquals(withIfaceName, ParcelUtils.parcelingRoundTrip(withIfaceName)); } @Test