From 97c36bc8c38ed69d135ea5832d4a9f25ba10f3fe Mon Sep 17 00:00:00 2001 From: Milim Lee Date: Thu, 17 Oct 2019 05:02:33 +0900 Subject: [PATCH] Local Tethering with ncm interface Bug: 130840842 Test: build, boot atest TetheringTest manual test (call startTethering(TETHERING_NCM)) Change-Id: Icc6c4d6be39e787503cecf3a5835b40d4be12a57 --- .../src/android/net/TetheringManager.java | 6 ++ Tethering/res/values/config.xml | 6 ++ Tethering/res/values/overlayable.xml | 1 + Tethering/src/android/net/ip/IpServer.java | 3 +- .../connectivity/tethering/Tethering.java | 21 +++++++ .../tethering/TetheringConfiguration.java | 8 +++ .../connectivity/tethering/TetheringTest.java | 59 ++++++++++++++++--- 7 files changed, 96 insertions(+), 8 deletions(-) diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index 8dacecc4ff..79c6930404 100644 --- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -130,6 +130,12 @@ public class TetheringManager { */ public static final int TETHERING_WIFI_P2P = 3; + /** + * Ncm local tethering type. + * @see #startTethering(TetheringRequest, Executor, StartTetheringCallback) + */ + public static final int TETHERING_NCM = 4; + public static final int TETHER_ERROR_NO_ERROR = 0; public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; diff --git a/Tethering/res/values/config.xml b/Tethering/res/values/config.xml index 19e8d696c3..c489cbcd5a 100644 --- a/Tethering/res/values/config.xml +++ b/Tethering/res/values/config.xml @@ -28,6 +28,12 @@ "rndis\\d" + + + + diff --git a/Tethering/res/values/overlayable.xml b/Tethering/res/values/overlayable.xml index e089d9d199..fe025c7ac9 100644 --- a/Tethering/res/values/overlayable.xml +++ b/Tethering/res/values/overlayable.xml @@ -17,6 +17,7 @@ + diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index 4306cf0bd5..baee99a5a5 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -416,7 +416,8 @@ public class IpServer extends StateMachine { final Inet4Address srvAddr; int prefixLen = 0; try { - if (mInterfaceType == TetheringManager.TETHERING_USB) { + if (mInterfaceType == TetheringManager.TETHERING_USB + || mInterfaceType == TetheringManager.TETHERING_NCM) { srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR); prefixLen = USB_PREFIX_LENGTH; } else if (mInterfaceType == TetheringManager.TETHERING_WIFI) { diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index fdc045fb15..f4d083194d 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -19,6 +19,7 @@ package com.android.server.connectivity.tethering; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; +import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; @@ -30,6 +31,7 @@ import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER; import static android.net.TetheringManager.EXTRA_ERRORED_TETHER; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_INVALID; +import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; @@ -407,6 +409,8 @@ public class Tethering { return TETHERING_USB; } else if (cfg.isBluetooth(iface)) { return TETHERING_BLUETOOTH; + } else if (cfg.isNcm(iface)) { + return TETHERING_NCM; } return TETHERING_INVALID; } @@ -455,6 +459,10 @@ public class Tethering { case TETHERING_BLUETOOTH: setBluetoothTethering(enable, listener); break; + case TETHERING_NCM: + result = setNcmTethering(enable); + sendTetherResult(listener, result); + break; default: Log.w(TAG, "Invalid tether type."); sendTetherResult(listener, TETHER_ERROR_UNKNOWN_IFACE); @@ -804,6 +812,7 @@ public class Tethering { final boolean usbConnected = intent.getBooleanExtra(USB_CONNECTED, false); final boolean usbConfigured = intent.getBooleanExtra(USB_CONFIGURED, false); final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false); + final boolean ncmEnabled = intent.getBooleanExtra(USB_FUNCTION_NCM, false); mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s", usbConnected, usbConfigured, rndisEnabled)); @@ -831,6 +840,8 @@ public class Tethering { } else if (usbConfigured && rndisEnabled) { // Tether if rndis is enabled and usb is configured. tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB); + } else if (usbConnected && ncmEnabled) { + tetherMatchingInterfaces(IpServer.STATE_LOCAL_ONLY, TETHERING_NCM); } mRndisEnabled = usbConfigured && rndisEnabled; } @@ -1118,6 +1129,16 @@ public class Tethering { return TETHER_ERROR_NO_ERROR; } + private int setNcmTethering(boolean enable) { + if (VDBG) Log.d(TAG, "setNcmTethering(" + enable + ")"); + UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE); + synchronized (mPublicSync) { + usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_NCM + : UsbManager.FUNCTION_NONE); + } + return TETHER_ERROR_NO_ERROR; + } + // TODO review API - figure out how to delete these entirely. String[] getTetheredIfaces() { ArrayList list = new ArrayList(); diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java index 068c346fbf..7e9e26f5af 100644 --- a/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -83,6 +83,7 @@ public class TetheringConfiguration { public final String[] tetherableWifiRegexs; public final String[] tetherableWifiP2pRegexs; public final String[] tetherableBluetoothRegexs; + public final String[] tetherableNcmRegexs; public final boolean isDunRequired; public final boolean chooseUpstreamAutomatically; public final Collection preferredUpstreamIfaceTypes; @@ -103,6 +104,7 @@ public class TetheringConfiguration { Resources res = getResources(ctx, activeDataSubId); tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs); + tetherableNcmRegexs = getResourceStringArray(res, R.array.config_tether_ncm_regexs); // TODO: Evaluate deleting this altogether now that Wi-Fi always passes // us an interface name. Careful consideration needs to be given to // implications for Settings and for provisioning checks. @@ -156,6 +158,11 @@ public class TetheringConfiguration { return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs); } + /** Check if interface is ncm */ + public boolean isNcm(String iface) { + return matchesDownstreamRegexs(iface, tetherableNcmRegexs); + } + /** Check whether no ui entitlement application is available.*/ public boolean hasMobileHotspotProvisionApp() { return !TextUtils.isEmpty(provisioningAppNoUi); @@ -170,6 +177,7 @@ public class TetheringConfiguration { dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs); dumpStringArray(pw, "tetherableWifiP2pRegexs", tetherableWifiP2pRegexs); dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs); + dumpStringArray(pw, "tetherableNcmRegexs", tetherableNcmRegexs); pw.print("isDunRequired: "); pw.println(isDunRequired); diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index 932fd50622..8440562aae 100644 --- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -18,6 +18,7 @@ package com.android.server.connectivity.tethering; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; +import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; @@ -27,6 +28,7 @@ import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; 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; +import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; @@ -151,6 +153,7 @@ public class TetheringTest { private static final String TEST_USB_IFNAME = "test_rndis0"; private static final String TEST_WLAN_IFNAME = "test_wlan0"; private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0"; + private static final String TEST_NCM_IFNAME = "test_ncm0"; private static final String TETHERING_NAME = "Tethering"; private static final int DHCPSERVER_START_TIMEOUT_MS = 1000; @@ -252,9 +255,11 @@ public class TetheringTest { ifName.equals(TEST_USB_IFNAME) || ifName.equals(TEST_WLAN_IFNAME) || ifName.equals(TEST_MOBILE_IFNAME) - || ifName.equals(TEST_P2P_IFNAME)); + || ifName.equals(TEST_P2P_IFNAME) + || ifName.equals(TEST_NCM_IFNAME)); final String[] ifaces = new String[] { - TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME}; + TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME, + TEST_NCM_IFNAME}; return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET, MacAddress.ALL_ZEROS_ADDRESS); } @@ -428,13 +433,16 @@ public class TetheringTest { .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" }); when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) .thenReturn(new String[0]); + when(mResources.getStringArray(R.array.config_tether_ncm_regexs)) + .thenReturn(new String[] { "test_ncm\\d" }); when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false); when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( false); when(mNetd.interfaceGetList()) .thenReturn(new String[] { - TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME}); + TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME, + TEST_NCM_IFNAME}); when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn(""); mInterfaceConfiguration = new InterfaceConfigurationParcel(); mInterfaceConfiguration.flags = new String[0]; @@ -522,11 +530,16 @@ public class TetheringTest { P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST); } - private void sendUsbBroadcast(boolean connected, boolean configured, boolean rndisFunction) { + private void sendUsbBroadcast(boolean connected, boolean configured, boolean function, + int type) { final Intent intent = new Intent(UsbManager.ACTION_USB_STATE); intent.putExtra(USB_CONNECTED, connected); intent.putExtra(USB_CONFIGURED, configured); - intent.putExtra(USB_FUNCTION_RNDIS, rndisFunction); + if (type == TETHERING_USB) { + intent.putExtra(USB_FUNCTION_RNDIS, function); + } else { + intent.putExtra(USB_FUNCTION_NCM, function); + } mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } @@ -576,6 +589,15 @@ public class TetheringTest { verifyNoMoreInteractions(mWifiManager); } + private void prepareNcmTethering() { + // Emulate startTethering(TETHERING_NCM) called + mTethering.startTethering(createTetheringRquestParcel(TETHERING_NCM), null); + mLooper.dispatchAll(); + verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM); + + mTethering.interfaceStatusChanged(TEST_NCM_IFNAME, true); + } + private void prepareUsbTethering(UpstreamNetworkState upstreamState) { when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) @@ -598,7 +620,7 @@ public class TetheringTest { verifyNoMoreInteractions(mNetd); // Pretend we then receive USB configured broadcast. - sendUsbBroadcast(true, true, true); + sendUsbBroadcast(true, true, true, TETHERING_USB); mLooper.dispatchAll(); // Now we should see the start of tethering mechanics (in this case: // tetherMatchingInterfaces() which starts by fetching all interfaces). @@ -689,7 +711,7 @@ public class TetheringTest { private void runUsbTethering(UpstreamNetworkState upstreamState) { prepareUsbTethering(upstreamState); - sendUsbBroadcast(true, true, true); + sendUsbBroadcast(true, true, true, TETHERING_USB); mLooper.dispatchAll(); } @@ -812,6 +834,29 @@ public class TetheringTest { verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network); } + private void runNcmTethering() { + prepareNcmTethering(); + sendUsbBroadcast(true, true, true, TETHERING_NCM); + mLooper.dispatchAll(); + } + + @Test + public void workingNcmTethering() throws Exception { + runNcmTethering(); + + verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); + } + + @Test + public void workingNcmTethering_LegacyDhcp() { + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + true); + sendConfigurationChanged(); + runNcmTethering(); + + verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any()); + } + @Test public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception { workingLocalOnlyHotspotEnrichedApBroadcast(true);