diff --git a/Tethering/common/TetheringLib/api/system-current.txt b/Tethering/common/TetheringLib/api/system-current.txt index edd1ebb5f7..105bab11d5 100644 --- a/Tethering/common/TetheringLib/api/system-current.txt +++ b/Tethering/common/TetheringLib/api/system-current.txt @@ -27,6 +27,8 @@ package android.net { method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int); method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback); field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; + field public static final int CONNECTIVITY_SCOPE_GLOBAL = 1; // 0x1 + field public static final int CONNECTIVITY_SCOPE_LOCAL = 2; // 0x2 field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; field public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; field public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; @@ -72,6 +74,7 @@ package android.net { public static interface TetheringManager.TetheringEventCallback { method public default void onClientsChanged(@NonNull java.util.Collection); method public default void onError(@NonNull String, int); + method public default void onLocalOnlyInterfacesChanged(@NonNull java.util.List); method public default void onOffloadStatusChanged(int); method public default void onTetherableInterfacesChanged(@NonNull java.util.List); method public default void onTetheredInterfacesChanged(@NonNull java.util.List); @@ -81,6 +84,7 @@ package android.net { public static class TetheringManager.TetheringRequest { method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); + method public int getConnectivityScope(); method @Nullable public android.net.LinkAddress getLocalIpv4Address(); method public boolean getShouldShowEntitlementUi(); method public int getTetheringType(); @@ -90,6 +94,7 @@ package android.net { public static class TetheringManager.TetheringRequest.Builder { ctor public TetheringManager.TetheringRequest.Builder(int); method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setConnectivityScope(int); 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 setShouldShowEntitlementUi(boolean); 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/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index 97fb4974d0..c64da8a8e7 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -556,6 +556,28 @@ public class TetheringManager { }); } + /** + * Indicates that this tethering connection will provide connectivity beyond this device (e.g., + * global Internet access). + */ + public static final int CONNECTIVITY_SCOPE_GLOBAL = 1; + + /** + * Indicates that this tethering connection will only provide local connectivity. + */ + public static final int CONNECTIVITY_SCOPE_LOCAL = 2; + + /** + * Connectivity scopes for {@link TetheringRequest.Builder#setConnectivityScope}. + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "CONNECTIVITY_SCOPE_", value = { + CONNECTIVITY_SCOPE_GLOBAL, + CONNECTIVITY_SCOPE_LOCAL, + }) + public @interface ConnectivityScope {} + /** * Use with {@link #startTethering} to specify additional parameters when starting tethering. */ @@ -579,6 +601,7 @@ public class TetheringManager { mBuilderParcel.staticClientAddress = null; mBuilderParcel.exemptFromEntitlementCheck = false; mBuilderParcel.showProvisioningUi = true; + mBuilderParcel.connectivityScope = getDefaultConnectivityScope(type); } /** @@ -624,7 +647,21 @@ public class TetheringManager { return this; } - /** Build {@link TetheringRequest] with the currently set configuration. */ + /** + * Sets the connectivity scope to be provided by this tethering downstream. + */ + @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) + @NonNull + public Builder setConnectivityScope(@ConnectivityScope int scope) { + if (!checkConnectivityScope(mBuilderParcel.tetheringType, scope)) { + throw new IllegalArgumentException("Invalid connectivity scope " + scope); + } + + mBuilderParcel.connectivityScope = scope; + return this; + } + + /** Build {@link TetheringRequest} with the currently set configuration. */ @NonNull public TetheringRequest build() { return new TetheringRequest(mBuilderParcel); @@ -655,6 +692,12 @@ public class TetheringManager { return mRequestParcel.tetheringType; } + /** Get connectivity type */ + @ConnectivityScope + public int getConnectivityScope() { + return mRequestParcel.connectivityScope; + } + /** Check if exempt from entitlement check. */ public boolean isExemptFromEntitlementCheck() { return mRequestParcel.exemptFromEntitlementCheck; @@ -678,6 +721,26 @@ public class TetheringManager { new IpPrefix(clientAddress.toString())); } + /** + * Returns the default connectivity scope for the given tethering type. Usually this is + * CONNECTIVITY_SCOPE_GLOBAL, except for NCM which for historical reasons defaults to local. + * @hide + */ + public static @ConnectivityScope int getDefaultConnectivityScope(int tetheringType) { + return tetheringType != TETHERING_NCM + ? CONNECTIVITY_SCOPE_GLOBAL + : CONNECTIVITY_SCOPE_LOCAL; + } + + /** + * Checks whether the requested connectivity scope is allowed. + * @hide + */ + private static boolean checkConnectivityScope(int type, int scope) { + if (scope == CONNECTIVITY_SCOPE_GLOBAL) return true; + return type == TETHERING_USB || type == TETHERING_ETHERNET || type == TETHERING_NCM; + } + /** * Get a TetheringRequestParcel from the configuration * @hide @@ -939,6 +1002,15 @@ public class TetheringManager { */ default void onTetheredInterfacesChanged(@NonNull List interfaces) {} + /** + * Called when there was a change in the list of local-only interfaces. + * + *

This will be called immediately after the callback is registered, and may be called + * multiple times later upon changes. + * @param interfaces The list of 0 or more String of active local-only interface names. + */ + default void onLocalOnlyInterfacesChanged(@NonNull List interfaces) {} + /** * Called when an error occurred configuring tethering. * @@ -1045,6 +1117,7 @@ public class TetheringManager { private final HashMap mErrorStates = new HashMap<>(); private String[] mLastTetherableInterfaces = null; private String[] mLastTetheredInterfaces = null; + private String[] mLastLocalOnlyInterfaces = null; @Override public void onUpstreamChanged(Network network) throws RemoteException { @@ -1082,6 +1155,14 @@ public class TetheringManager { Collections.unmodifiableList(Arrays.asList(mLastTetheredInterfaces))); } + private synchronized void maybeSendLocalOnlyIfacesChangedCallback( + final TetherStatesParcel newStates) { + if (Arrays.equals(mLastLocalOnlyInterfaces, newStates.localOnlyList)) return; + mLastLocalOnlyInterfaces = newStates.localOnlyList.clone(); + callback.onLocalOnlyInterfacesChanged( + Collections.unmodifiableList(Arrays.asList(mLastLocalOnlyInterfaces))); + } + // Called immediately after the callbacks are registered. @Override public void onCallbackStarted(TetheringCallbackStartedParcel parcel) { @@ -1092,6 +1173,7 @@ public class TetheringManager { sendRegexpsChanged(parcel.config); maybeSendTetherableIfacesChangedCallback(parcel.states); maybeSendTetheredIfacesChangedCallback(parcel.states); + maybeSendLocalOnlyIfacesChangedCallback(parcel.states); callback.onClientsChanged(parcel.tetheredClients); callback.onOffloadStatusChanged(parcel.offloadStatus); }); @@ -1122,6 +1204,7 @@ public class TetheringManager { sendErrorCallbacks(states); maybeSendTetherableIfacesChangedCallback(states); maybeSendTetheredIfacesChangedCallback(states); + maybeSendLocalOnlyIfacesChangedCallback(states); }); } diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl b/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl index c0280d3dbf..f13c970d28 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl +++ b/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl @@ -28,4 +28,5 @@ parcelable TetheringRequestParcel { LinkAddress staticClientAddress; boolean exemptFromEntitlementCheck; boolean showProvisioningUi; + int connectivityScope; } diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index da15fa8862..c45ce830e2 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -1426,7 +1426,7 @@ public class IpServer extends StateMachine { break; case CMD_INTERFACE_DOWN: transitionTo(mUnavailableState); - mLog.i("Untethered (interface down) and restarting" + mIfaceName); + mLog.i("Untethered (interface down) and restarting " + mIfaceName); mCallback.requestEnableTethering(mInterfaceType, true /* enabled */); break; default: diff --git a/Tethering/src/android/net/util/TetheringUtils.java b/Tethering/src/android/net/util/TetheringUtils.java index 9e7cc2fc14..29900d9beb 100644 --- a/Tethering/src/android/net/util/TetheringUtils.java +++ b/Tethering/src/android/net/util/TetheringUtils.java @@ -162,7 +162,8 @@ public class TetheringUtils { && Objects.equals(request.localIPv4Address, otherRequest.localIPv4Address) && Objects.equals(request.staticClientAddress, otherRequest.staticClientAddress) && request.exemptFromEntitlementCheck == otherRequest.exemptFromEntitlementCheck - && request.showProvisioningUi == otherRequest.showProvisioningUi; + && request.showProvisioningUi == otherRequest.showProvisioningUi + && request.connectivityScope == otherRequest.connectivityScope; } /** Get inet6 address for all nodes given scope ID. */ diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 1815ff327e..acbfa8c670 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -29,6 +29,7 @@ import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO; import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; +import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL; import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY; import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER; import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER; @@ -90,6 +91,7 @@ import android.net.TetherStatesParcel; import android.net.TetheredClient; import android.net.TetheringCallbackStartedParcel; import android.net.TetheringConfigurationParcel; +import android.net.TetheringManager.TetheringRequest; import android.net.TetheringRequestParcel; import android.net.ip.IpServer; import android.net.shared.NetdUtils; @@ -731,7 +733,7 @@ public class Tethering { return; } maybeTrackNewInterfaceLocked(iface, TETHERING_ETHERNET); - changeInterfaceState(iface, IpServer.STATE_TETHERED); + changeInterfaceState(iface, getRequestedState(TETHERING_ETHERNET)); mConfiguredEthernetIface = iface; } } @@ -748,10 +750,10 @@ public class Tethering { } } - void tether(String iface, final IIntResultListener listener) { + void tether(String iface, int requestedState, final IIntResultListener listener) { mHandler.post(() -> { try { - listener.onResult(tether(iface, IpServer.STATE_TETHERED)); + listener.onResult(tether(iface, requestedState)); } catch (RemoteException e) { } }); } @@ -855,6 +857,22 @@ public class Tethering { return true; } + private int getRequestedState(int type) { + final TetheringRequestParcel request = mActiveTetheringRequests.get(type); + + // The request could have been deleted before we had a chance to complete it. + // If so, assume that the scope is the default scope for this tethering type. + // This likely doesn't matter - if the request has been deleted, then tethering is + // likely going to be stopped soon anyway. + final int connectivityScope = (request != null) + ? request.connectivityScope + : TetheringRequest.getDefaultConnectivityScope(type); + + return connectivityScope == CONNECTIVITY_SCOPE_LOCAL + ? IpServer.STATE_LOCAL_ONLY + : IpServer.STATE_TETHERED; + } + // TODO: Figure out how to update for local hotspot mode interfaces. private void sendTetherStateChangedBroadcast() { if (!isTetheringSupported()) return; @@ -994,9 +1012,11 @@ public class Tethering { mEntitlementMgr.stopProvisioningIfNeeded(TETHERING_USB); } else if (usbConfigured && rndisEnabled) { // Tether if rndis is enabled and usb is configured. - tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB); + final int state = getRequestedState(TETHERING_USB); + tetherMatchingInterfaces(state, TETHERING_USB); } else if (usbConnected && ncmEnabled) { - tetherMatchingInterfaces(IpServer.STATE_LOCAL_ONLY, TETHERING_NCM); + final int state = getRequestedState(TETHERING_NCM); + tetherMatchingInterfaces(state, TETHERING_NCM); } mRndisEnabled = usbConfigured && rndisEnabled; } diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java index 1906ca7bd0..e36df7fac2 100644 --- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java +++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java @@ -105,7 +105,7 @@ public class TetheringService extends Service { IIntResultListener listener) { if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return; - mTethering.tether(iface, listener); + mTethering.tether(iface, IpServer.STATE_TETHERED, listener); } @Override diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index d206ea0b4d..fe4e696708 100644 --- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -19,7 +19,14 @@ package android.net; import static android.Manifest.permission.MANAGE_TEST_NETWORKS; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.TETHER_PRIVILEGED; +import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL; +import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL; import static android.net.TetheringManager.TETHERING_ETHERNET; +import static android.system.OsConstants.IPPROTO_ICMPV6; + +import static com.android.net.module.util.ConnectivityUtils.isIPv6ULA; +import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6; +import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -50,6 +57,10 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; +import com.android.net.module.util.Struct; +import com.android.net.module.util.structs.EthernetHeader; +import com.android.net.module.util.structs.Icmpv6Header; +import com.android.net.module.util.structs.Ipv6Header; import com.android.testutils.HandlerUtils; import com.android.testutils.TapPacketReader; @@ -60,6 +71,7 @@ import org.junit.runner.RunWith; import java.io.FileDescriptor; import java.net.Inet4Address; +import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.NetworkInterface; import java.net.SocketException; @@ -229,6 +241,82 @@ public class EthernetTetheringTest { } + private static boolean isRouterAdvertisement(byte[] pkt) { + if (pkt == null) return false; + + ByteBuffer buf = ByteBuffer.wrap(pkt); + + final EthernetHeader ethHdr = Struct.parse(EthernetHeader.class, buf); + if (ethHdr.etherType != ETHER_TYPE_IPV6) return false; + + final Ipv6Header ipv6Hdr = Struct.parse(Ipv6Header.class, buf); + if (ipv6Hdr.nextHeader != (byte) IPPROTO_ICMPV6) return false; + + final Icmpv6Header icmpv6Hdr = Struct.parse(Icmpv6Header.class, buf); + return icmpv6Hdr.type == (short) ICMPV6_ROUTER_ADVERTISEMENT; + } + + private static void expectRouterAdvertisement(TapPacketReader reader, String iface, + long timeoutMs) { + final long deadline = SystemClock.uptimeMillis() + timeoutMs; + do { + byte[] pkt = reader.popPacket(timeoutMs); + if (isRouterAdvertisement(pkt)) return; + timeoutMs = deadline - SystemClock.uptimeMillis(); + } while (timeoutMs > 0); + fail("Did not receive router advertisement on " + iface + " after " + + timeoutMs + "ms idle"); + } + + private static void expectLocalOnlyAddresses(String iface) throws Exception { + final List interfaceAddresses = + NetworkInterface.getByName(iface).getInterfaceAddresses(); + + boolean foundIpv6Ula = false; + for (InterfaceAddress ia : interfaceAddresses) { + final InetAddress addr = ia.getAddress(); + if (isIPv6ULA(addr)) { + foundIpv6Ula = true; + } + final int prefixlen = ia.getNetworkPrefixLength(); + final LinkAddress la = new LinkAddress(addr, prefixlen); + if (la.isIpv6() && la.isGlobalPreferred()) { + fail("Found global IPv6 address on local-only interface: " + interfaceAddresses); + } + } + + assertTrue("Did not find IPv6 ULA on local-only interface " + iface, + foundIpv6Ula); + } + + @Test + public void testLocalOnlyTethering() throws Exception { + assumeFalse(mEm.isAvailable()); + + mEm.setIncludeTestInterfaces(true); + + mTestIface = createTestInterface(); + + final String iface = mTetheredInterfaceRequester.getInterface(); + assertEquals("TetheredInterfaceCallback for unexpected interface", + mTestIface.getInterfaceName(), iface); + + final TetheringRequest request = new TetheringRequest.Builder(TETHERING_ETHERNET) + .setConnectivityScope(CONNECTIVITY_SCOPE_LOCAL).build(); + mTetheringEventCallback = enableEthernetTethering(iface, request); + mTetheringEventCallback.awaitInterfaceLocalOnly(); + + // makePacketReader only works after tethering is started, because until then the interface + // does not have an IP address, and unprivileged apps cannot see interfaces without IP + // addresses. This shouldn't be flaky because the TAP interface will buffer all packets even + // before the reader is started. + FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor(); + mTapPacketReader = makePacketReader(fd, getMTU(mTestIface)); + + expectRouterAdvertisement(mTapPacketReader, iface, 2000 /* timeoutMs */); + expectLocalOnlyAddresses(iface); + } + private boolean isAdbOverNetwork() { // If adb TCP port opened, this test may running by adb over network. return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1) @@ -257,10 +345,13 @@ public class EthernetTetheringTest { private final TetheringManager mTm; private final CountDownLatch mTetheringStartedLatch = new CountDownLatch(1); private final CountDownLatch mTetheringStoppedLatch = new CountDownLatch(1); + private final CountDownLatch mLocalOnlyStartedLatch = new CountDownLatch(1); + private final CountDownLatch mLocalOnlyStoppedLatch = new CountDownLatch(1); private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1); private final String mIface; private volatile boolean mInterfaceWasTethered = false; + private volatile boolean mInterfaceWasLocalOnly = false; private volatile boolean mUnregistered = false; private volatile Collection mClients = null; @@ -279,7 +370,6 @@ public class EthernetTetheringTest { // Ignore stale callbacks registered by previous test cases. if (mUnregistered) return; - final boolean wasTethered = mTetheringStartedLatch.getCount() == 0; if (!mInterfaceWasTethered && (mIface == null || interfaces.contains(mIface))) { // This interface is being tethered for the first time. Log.d(TAG, "Tethering started: " + interfaces); @@ -291,20 +381,48 @@ public class EthernetTetheringTest { } } + @Override + public void onLocalOnlyInterfacesChanged(List interfaces) { + // Ignore stale callbacks registered by previous test cases. + if (mUnregistered) return; + + if (!mInterfaceWasLocalOnly && (mIface == null || interfaces.contains(mIface))) { + // This interface is being put into local-only mode for the first time. + Log.d(TAG, "Local-only started: " + interfaces); + mInterfaceWasLocalOnly = true; + mLocalOnlyStartedLatch.countDown(); + } else if (mInterfaceWasLocalOnly && !interfaces.contains(mIface)) { + Log.d(TAG, "Local-only stopped: " + interfaces); + mLocalOnlyStoppedLatch.countDown(); + } + } + public void awaitInterfaceTethered() throws Exception { assertTrue("Ethernet not tethered after " + TIMEOUT_MS + "ms", mTetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); } + public void awaitInterfaceLocalOnly() throws Exception { + assertTrue("Ethernet not local-only after " + TIMEOUT_MS + "ms", + mLocalOnlyStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + } + public void awaitInterfaceUntethered() throws Exception { // Don't block teardown if the interface was never tethered. // This is racy because the interface might become tethered right after this check, but // that can only happen in tearDown if startTethering timed out, which likely means // the test has already failed. - if (!mInterfaceWasTethered) return; + if (!mInterfaceWasTethered && !mInterfaceWasLocalOnly) return; - assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms", - mTetheringStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + if (mInterfaceWasTethered) { + assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms", + mTetheringStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + } else if (mInterfaceWasLocalOnly) { + assertTrue(mIface + " not untethered after " + TIMEOUT_MS + "ms", + mLocalOnlyStoppedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + } else { + fail(mIface + " cannot be both tethered and local-only. Update this test class."); + } } @Override @@ -347,7 +465,19 @@ public class EthernetTetheringTest { }; Log.d(TAG, "Starting Ethernet tethering"); mTm.startTethering(request, mHandler::post /* executor */, startTetheringCallback); - callback.awaitInterfaceTethered(); + + final int connectivityType = request.getConnectivityScope(); + switch (connectivityType) { + case CONNECTIVITY_SCOPE_GLOBAL: + callback.awaitInterfaceTethered(); + break; + case CONNECTIVITY_SCOPE_LOCAL: + callback.awaitInterfaceLocalOnly(); + break; + default: + fail("Unexpected connectivity type requested: " + connectivityType); + } + return callback; } @@ -444,7 +574,6 @@ public class EthernetTetheringTest { } private static final class TetheredInterfaceRequester implements TetheredInterfaceCallback { - private final CountDownLatch mInterfaceAvailableLatch = new CountDownLatch(1); private final Handler mHandler; private final EthernetManager mEm; diff --git a/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java b/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java index 9968b5f173..e5d0b1cd38 100644 --- a/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java +++ b/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java @@ -15,6 +15,7 @@ */ package android.net.util; +import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.system.OsConstants.AF_UNIX; @@ -78,7 +79,7 @@ public class TetheringUtilsTest { } @Test - public void testIsTetheringRequestEquals() throws Exception { + public void testIsTetheringRequestEquals() { TetheringRequestParcel request = makeTetheringRequestParcel(); assertTrue(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, mTetheringRequest)); @@ -104,7 +105,11 @@ public class TetheringUtilsTest { request.showProvisioningUi = false; assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); - MiscAsserts.assertFieldCountEquals(5, TetheringRequestParcel.class); + request = makeTetheringRequestParcel(); + request.connectivityScope = CONNECTIVITY_SCOPE_LOCAL; + assertFalse(TetheringUtils.isTetheringRequestEquals(mTetheringRequest, request)); + + MiscAsserts.assertFieldCountEquals(6, TetheringRequestParcel.class); } // Writes the specified packet to a filedescriptor, skipping the Ethernet header. 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 be98f6012f..7204ff6747 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java @@ -37,6 +37,7 @@ import android.net.IIntResultListener; import android.net.ITetheringConnector; import android.net.ITetheringEventCallback; import android.net.TetheringRequestParcel; +import android.net.ip.IpServer; import android.os.Bundle; import android.os.Handler; import android.os.ResultReceiver; @@ -158,7 +159,7 @@ public final class TetheringServiceTest { private void runTether(final TestTetheringResult result) throws Exception { mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result); verify(mTethering).isTetheringSupported(); - verify(mTethering).tether(eq(TEST_IFACE_NAME), eq(result)); + verify(mTethering).tether(TEST_IFACE_NAME, IpServer.STATE_TETHERED, result); } @Test 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 b207af911d..7c3dd23243 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -33,6 +33,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.RouteInfo.RTN_UNICAST; import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; +import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL; import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY; import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER; import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER; @@ -702,17 +703,19 @@ public class TetheringTest { } private TetheringRequestParcel createTetheringRequestParcel(final int type) { - return createTetheringRequestParcel(type, null, null, false); + return createTetheringRequestParcel(type, null, null, false, CONNECTIVITY_SCOPE_GLOBAL); } private TetheringRequestParcel createTetheringRequestParcel(final int type, - final LinkAddress serverAddr, final LinkAddress clientAddr, final boolean exempt) { + final LinkAddress serverAddr, final LinkAddress clientAddr, final boolean exempt, + final int scope) { final TetheringRequestParcel request = new TetheringRequestParcel(); request.tetheringType = type; request.localIPv4Address = serverAddr; request.staticClientAddress = clientAddr; request.exemptFromEntitlementCheck = exempt; request.showProvisioningUi = false; + request.connectivityScope = scope; return request; } @@ -1973,16 +1976,14 @@ public class TetheringTest { 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, false), firstResult); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), 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, false), secondResult); + mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), secondResult); mLooper.dispatchAll(); secondResult.assertHasResult(); verify(mUsbManager, never()).setCurrentFunctions(UsbManager.FUNCTION_NONE); @@ -1991,7 +1992,7 @@ public class TetheringTest { // Enable USB tethering with a different request and expect that USB is stopped and // started. mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, - serverLinkAddr, clientLinkAddr, false), thirdResult); + serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL), thirdResult); mLooper.dispatchAll(); thirdResult.assertHasResult(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE); @@ -2014,7 +2015,7 @@ public class TetheringTest { final ArgumentCaptor dhcpParamsCaptor = ArgumentCaptor.forClass(DhcpServingParamsParcel.class); mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB, - serverLinkAddr, clientLinkAddr, false), null); + serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL), null); mLooper.dispatchAll(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); @@ -2080,7 +2081,8 @@ public class TetheringTest { public void testExemptFromEntitlementCheck() throws Exception { setupForRequiredProvisioning(); final TetheringRequestParcel wifiNotExemptRequest = - createTetheringRequestParcel(TETHERING_WIFI, null, null, false); + createTetheringRequestParcel(TETHERING_WIFI, null, null, false, + CONNECTIVITY_SCOPE_GLOBAL); mTethering.startTethering(wifiNotExemptRequest, null); mLooper.dispatchAll(); verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false); @@ -2093,7 +2095,8 @@ public class TetheringTest { setupForRequiredProvisioning(); final TetheringRequestParcel wifiExemptRequest = - createTetheringRequestParcel(TETHERING_WIFI, null, null, true); + createTetheringRequestParcel(TETHERING_WIFI, null, null, true, + CONNECTIVITY_SCOPE_GLOBAL); mTethering.startTethering(wifiExemptRequest, null); mLooper.dispatchAll(); verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false); @@ -2386,7 +2389,7 @@ public class TetheringTest { mTethering.interfaceStatusChanged(TEST_BT_IFNAME, false); mTethering.interfaceStatusChanged(TEST_BT_IFNAME, true); final ResultListener tetherResult = new ResultListener(TETHER_ERROR_NO_ERROR); - mTethering.tether(TEST_BT_IFNAME, tetherResult); + mTethering.tether(TEST_BT_IFNAME, IpServer.STATE_TETHERED, tetherResult); mLooper.dispatchAll(); tetherResult.assertHasResult();