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/common/TetheringLib/api/system-current.txt b/Tethering/common/TetheringLib/api/system-current.txt
index edd1ebb..105bab1 100644
--- a/Tethering/common/TetheringLib/api/system-current.txt
+++ b/Tethering/common/TetheringLib/api/system-current.txt
@@ -27,6 +27,8 @@
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 @@
public static interface TetheringManager.TetheringEventCallback {
method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
method public default void onError(@NonNull String, int);
+ method public default void onLocalOnlyInterfacesChanged(@NonNull java.util.List<java.lang.String>);
method public default void onOffloadStatusChanged(int);
method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
@@ -81,6 +84,7 @@
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 @@
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 97fb497..c64da8a 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -557,6 +557,28 @@
}
/**
+ * 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.
*/
public static class TetheringRequest {
@@ -579,6 +601,7 @@
mBuilderParcel.staticClientAddress = null;
mBuilderParcel.exemptFromEntitlementCheck = false;
mBuilderParcel.showProvisioningUi = true;
+ mBuilderParcel.connectivityScope = getDefaultConnectivityScope(type);
}
/**
@@ -624,7 +647,21 @@
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 @@
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;
@@ -679,6 +722,26 @@
}
/**
+ * 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
*/
@@ -940,6 +1003,15 @@
default void onTetheredInterfacesChanged(@NonNull List<String> interfaces) {}
/**
+ * Called when there was a change in the list of local-only interfaces.
+ *
+ * <p>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<String> interfaces) {}
+
+ /**
* Called when an error occurred configuring tethering.
*
* <p>This will be called immediately after the callback is registered if the latest status
@@ -1045,6 +1117,7 @@
private final HashMap<String, Integer> 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 @@
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 @@
sendRegexpsChanged(parcel.config);
maybeSendTetherableIfacesChangedCallback(parcel.states);
maybeSendTetheredIfacesChangedCallback(parcel.states);
+ maybeSendLocalOnlyIfacesChangedCallback(parcel.states);
callback.onClientsChanged(parcel.tetheredClients);
callback.onOffloadStatusChanged(parcel.offloadStatus);
});
@@ -1122,6 +1204,7 @@
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 c0280d3..f13c970 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl
@@ -28,4 +28,5 @@
LinkAddress staticClientAddress;
boolean exemptFromEntitlementCheck;
boolean showProvisioningUi;
+ int connectivityScope;
}