Allow callers of startTethering to choose local-only mode.

This is useful for OEMs that want to use RNDIS or NCM as a
local-only link that is directly connected to some other host.
This can be used to implement USB tethering using NCM, which
currently only supports local-only mode.

Bug: 175090447
Test: TetheringIntegrationTests:EthernetTetheringTest#testLocalOnlyTethering
Change-Id: I0ffaa46e4640e5b235340a15d25909106ceb0c07
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index da15fa8..c45ce83 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -1426,7 +1426,7 @@
                     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 9e7cc2f..29900d9 100644
--- a/Tethering/src/android/net/util/TetheringUtils.java
+++ b/Tethering/src/android/net/util/TetheringUtils.java
@@ -162,7 +162,8 @@
                 && 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 1815ff3..acbfa8c 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.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.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 @@
                     return;
                 }
                 maybeTrackNewInterfaceLocked(iface, TETHERING_ETHERNET);
-                changeInterfaceState(iface, IpServer.STATE_TETHERED);
+                changeInterfaceState(iface, getRequestedState(TETHERING_ETHERNET));
                 mConfiguredEthernetIface = iface;
             }
         }
@@ -748,10 +750,10 @@
         }
     }
 
-    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 @@
         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 @@
                     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 1906ca7..e36df7f 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@@ -105,7 +105,7 @@
                 IIntResultListener listener) {
             if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
 
-            mTethering.tether(iface, listener);
+            mTethering.tether(iface, IpServer.STATE_TETHERED, listener);
         }
 
         @Override