Merge "Local Tethering with ncm interface"

This commit is contained in:
Remi NGUYEN VAN
2020-01-24 13:59:55 +00:00
committed by Android (Google) Code Review
10 changed files with 121 additions and 9 deletions

View File

@@ -3803,10 +3803,12 @@ package android.hardware.usb {
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
field public static final long FUNCTION_NCM = 1024L; // 0x400L
field public static final long FUNCTION_NONE = 0L; // 0x0L
field public static final long FUNCTION_RNDIS = 32L; // 0x20L
field public static final String USB_CONFIGURED = "configured";
field public static final String USB_CONNECTED = "connected";
field public static final String USB_FUNCTION_NCM = "ncm";
field public static final String USB_FUNCTION_RNDIS = "rndis";
}
@@ -6564,6 +6566,7 @@ package android.net {
field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
field public static final int TETHERING_BLUETOOTH = 2; // 0x2
field public static final int TETHERING_INVALID = -1; // 0xffffffff
field public static final int TETHERING_NCM = 4; // 0x4
field public static final int TETHERING_USB = 1; // 0x1
field public static final int TETHERING_WIFI = 0; // 0x0
field public static final int TETHERING_WIFI_P2P = 3; // 0x3

View File

@@ -1759,6 +1759,7 @@ package android.net {
field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
field public static final int TETHERING_BLUETOOTH = 2; // 0x2
field public static final int TETHERING_INVALID = -1; // 0xffffffff
field public static final int TETHERING_NCM = 4; // 0x4
field public static final int TETHERING_USB = 1; // 0x1
field public static final int TETHERING_WIFI = 0; // 0x0
field public static final int TETHERING_WIFI_P2P = 3; // 0x3

View File

@@ -261,6 +261,15 @@ public class UsbManager {
*/
public static final String USB_FUNCTION_ACCESSORY = "accessory";
/**
* Name of the NCM USB function.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast
*
* {@hide}
*/
@SystemApi
public static final String USB_FUNCTION_NCM = "ncm";
/**
* Name of extra for {@link #ACTION_USB_PORT_CHANGED}
* containing the {@link UsbPort} object for the port.
@@ -367,8 +376,15 @@ public class UsbManager {
*/
public static final long FUNCTION_ADB = GadgetFunction.ADB;
/**
* Code for the ncm source usb function.
* {@hide}
*/
@SystemApi
public static final long FUNCTION_NCM = 1 << 10;
private static final long SETTABLE_FUNCTIONS = FUNCTION_MTP | FUNCTION_PTP | FUNCTION_RNDIS
| FUNCTION_MIDI;
| FUNCTION_MIDI | FUNCTION_NCM;
private static final Map<String, Long> FUNCTION_NAME_TO_CODE = new HashMap<>();
@@ -380,6 +396,7 @@ public class UsbManager {
FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ACCESSORY, FUNCTION_ACCESSORY);
FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_AUDIO_SOURCE, FUNCTION_AUDIO_SOURCE);
FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ADB, FUNCTION_ADB);
FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_NCM, FUNCTION_NCM);
}
private final Context mContext;
@@ -954,6 +971,9 @@ public class UsbManager {
if ((functions & FUNCTION_AUDIO_SOURCE) != 0) {
joiner.add(UsbManager.USB_FUNCTION_AUDIO_SOURCE);
}
if ((functions & FUNCTION_NCM) != 0) {
joiner.add(UsbManager.USB_FUNCTION_NCM);
}
if ((functions & FUNCTION_ADB) != 0) {
joiner.add(UsbManager.USB_FUNCTION_ADB);
}

View File

@@ -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;

View File

@@ -28,6 +28,12 @@
<item>"rndis\\d"</item>
</string-array>
<!-- List of regexpressions describing the interface (if any) that represent tetherable
NCM interfaces. If the device doesn't want to support tethering over NCM this should
be empty. -->
<string-array translatable="false" name="config_tether_ncm_regexs">
</string-array>
<!-- List of regexpressions describing the interface (if any) that represent tetherable
Wifi interfaces. If the device doesn't want to support tethering over Wifi this
should be empty. An example would be "softap.*" -->

View File

@@ -17,6 +17,7 @@
<overlayable name="TetheringConfig">
<policy type="product|system|vendor">
<item type="array" name="config_tether_usb_regexs"/>
<item type="array" name="config_tether_ncm_regexs" />
<item type="array" name="config_tether_wifi_regexs"/>
<item type="array" name="config_tether_wifi_p2p_regexs"/>
<item type="array" name="config_tether_bluetooth_regexs"/>

View File

@@ -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) {

View File

@@ -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;
@@ -408,6 +410,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;
}
@@ -456,6 +460,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);
@@ -805,6 +813,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));
@@ -832,6 +841,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;
}
@@ -1133,6 +1144,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<String> list = new ArrayList<String>();

View File

@@ -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<Integer> 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);

View File

@@ -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];
@@ -524,11 +532,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);
}
@@ -578,6 +591,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()))
@@ -600,7 +622,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).
@@ -691,7 +713,7 @@ public class TetheringTest {
private void runUsbTethering(UpstreamNetworkState upstreamState) {
prepareUsbTethering(upstreamState);
sendUsbBroadcast(true, true, true);
sendUsbBroadcast(true, true, true, TETHERING_USB);
mLooper.dispatchAll();
}
@@ -814,6 +836,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);