Merge changes Ie2c35190,I6bd9ceeb,I823c8e94,I1b4868ba into main am: d9a752d866
Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/3350070
Change-Id: I6c859205908ca5dd76b4f41e98b90aaff479b411
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Tethering/common/TetheringLib/api/system-current.txt b/Tethering/common/TetheringLib/api/system-current.txt
index 1728e16..0e85956 100644
--- a/Tethering/common/TetheringLib/api/system-current.txt
+++ b/Tethering/common/TetheringLib/api/system-current.txt
@@ -21,8 +21,10 @@
public final class TetheringInterface implements android.os.Parcelable {
ctor public TetheringInterface(int, @NonNull String);
+ ctor @FlaggedApi("com.android.net.flags.tethering_with_soft_ap_config") public TetheringInterface(int, @NonNull String, @Nullable android.net.wifi.SoftApConfiguration);
method public int describeContents();
method @NonNull public String getInterface();
+ method @FlaggedApi("com.android.net.flags.tethering_with_soft_ap_config") @Nullable @RequiresPermission(value=android.Manifest.permission.NETWORK_SETTINGS, conditional=true) public android.net.wifi.SoftApConfiguration getSoftApConfiguration();
method public int getType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheringInterface> CREATOR;
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringInterface.java b/Tethering/common/TetheringLib/src/android/net/TetheringInterface.java
index 84cdef1..0464fe0 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringInterface.java
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringInterface.java
@@ -16,13 +16,19 @@
package android.net;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.net.TetheringManager.TetheringType;
+import android.net.wifi.SoftApConfiguration;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.net.flags.Flags;
+
import java.util.Objects;
/**
@@ -33,15 +39,21 @@
public final class TetheringInterface implements Parcelable {
private final int mType;
private final String mInterface;
+ @Nullable
+ private final SoftApConfiguration mSoftApConfig;
+ @SuppressLint("UnflaggedApi")
public TetheringInterface(@TetheringType int type, @NonNull String iface) {
+ this(type, iface, null);
+ }
+
+ @FlaggedApi(Flags.FLAG_TETHERING_WITH_SOFT_AP_CONFIG)
+ public TetheringInterface(@TetheringType int type, @NonNull String iface,
+ @Nullable SoftApConfiguration softApConfig) {
Objects.requireNonNull(iface);
mType = type;
mInterface = iface;
- }
-
- private TetheringInterface(@NonNull Parcel in) {
- this(in.readInt(), in.readString());
+ mSoftApConfig = softApConfig;
}
/** Get tethering type. */
@@ -55,22 +67,36 @@
return mInterface;
}
+ /**
+ * Get the SoftApConfiguration provided for this interface, if any. This will only be populated
+ * for apps with the same uid that specified the configuration, or apps with permission
+ * {@link android.Manifest.permission.NETWORK_SETTINGS}.
+ */
+ @FlaggedApi(Flags.FLAG_TETHERING_WITH_SOFT_AP_CONFIG)
+ @RequiresPermission(value = android.Manifest.permission.NETWORK_SETTINGS, conditional = true)
+ @Nullable
+ public SoftApConfiguration getSoftApConfiguration() {
+ return mSoftApConfig;
+ }
+
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mType);
dest.writeString(mInterface);
+ dest.writeParcelable(mSoftApConfig, flags);
}
@Override
public int hashCode() {
- return Objects.hash(mType, mInterface);
+ return Objects.hash(mType, mInterface, mSoftApConfig);
}
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof TetheringInterface)) return false;
final TetheringInterface other = (TetheringInterface) obj;
- return mType == other.mType && mInterface.equals(other.mInterface);
+ return mType == other.mType && mInterface.equals(other.mInterface)
+ && Objects.equals(mSoftApConfig, other.mSoftApConfig);
}
@Override
@@ -82,8 +108,10 @@
public static final Creator<TetheringInterface> CREATOR = new Creator<TetheringInterface>() {
@NonNull
@Override
+ @SuppressLint("UnflaggedApi")
public TetheringInterface createFromParcel(@NonNull Parcel in) {
- return new TetheringInterface(in);
+ return new TetheringInterface(in.readInt(), in.readString(),
+ in.readParcelable(SoftApConfiguration.class.getClassLoader()));
}
@NonNull
@@ -97,6 +125,8 @@
@Override
public String toString() {
return "TetheringInterface {mType=" + mType
- + ", mInterface=" + mInterface + "}";
+ + ", mInterface=" + mInterface
+ + ((mSoftApConfig == null) ? "" : ", mSoftApConfig=" + mSoftApConfig)
+ + "}";
}
}
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 6b96397..bc771da 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -54,6 +54,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.StringJoiner;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
@@ -208,6 +209,20 @@
*/
public static final int MAX_TETHERING_TYPE = TETHERING_VIRTUAL;
+ private static String typeToString(@TetheringType int type) {
+ switch (type) {
+ case TETHERING_INVALID: return "TETHERING_INVALID";
+ case TETHERING_WIFI: return "TETHERING_WIFI";
+ case TETHERING_USB: return "TETHERING_USB";
+ case TETHERING_BLUETOOTH: return "TETHERING_BLUETOOTH";
+ case TETHERING_WIFI_P2P: return "TETHERING_WIFI_P2P";
+ case TETHERING_NCM: return "TETHERING_NCM";
+ case TETHERING_ETHERNET: return "TETHERING_ETHERNET";
+ default:
+ return "TETHERING_UNKNOWN(" + type + ")";
+ }
+ }
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
@@ -689,6 +704,17 @@
})
public @interface ConnectivityScope {}
+ private static String connectivityScopeToString(@ConnectivityScope int scope) {
+ switch (scope) {
+ case CONNECTIVITY_SCOPE_GLOBAL:
+ return "CONNECTIVITY_SCOPE_GLOBAL";
+ case CONNECTIVITY_SCOPE_LOCAL:
+ return "CONNECTIVITY_SCOPE_LOCAL";
+ default:
+ return "CONNECTIVITY_SCOPE_UNKNOWN(" + scope + ")";
+ }
+ }
+
/**
* Use with {@link #startTethering} to specify additional parameters when starting tethering.
*/
@@ -972,15 +998,31 @@
/** String of TetheringRequest detail. */
public String toString() {
- return "TetheringRequest [ type= " + mRequestParcel.tetheringType
- + ", localIPv4Address= " + mRequestParcel.localIPv4Address
- + ", staticClientAddress= " + mRequestParcel.staticClientAddress
- + ", exemptFromEntitlementCheck= " + mRequestParcel.exemptFromEntitlementCheck
- + ", showProvisioningUi= " + mRequestParcel.showProvisioningUi
- + ", softApConfig= " + mRequestParcel.softApConfig
- + ", uid= " + mRequestParcel.uid
- + ", packageName= " + mRequestParcel.packageName
- + " ]";
+ StringJoiner sj = new StringJoiner(", ", "TetheringRequest[ ", " ]");
+ sj.add(typeToString(mRequestParcel.tetheringType));
+ if (mRequestParcel.localIPv4Address != null) {
+ sj.add("localIpv4Address=" + mRequestParcel.localIPv4Address);
+ }
+ if (mRequestParcel.staticClientAddress != null) {
+ sj.add("staticClientAddress=" + mRequestParcel.staticClientAddress);
+ }
+ if (mRequestParcel.exemptFromEntitlementCheck) {
+ sj.add("exemptFromEntitlementCheck");
+ }
+ if (mRequestParcel.showProvisioningUi) {
+ sj.add("showProvisioningUi");
+ }
+ sj.add(connectivityScopeToString(mRequestParcel.connectivityScope));
+ if (mRequestParcel.softApConfig != null) {
+ sj.add("softApConfig=" + mRequestParcel.softApConfig);
+ }
+ if (mRequestParcel.uid != Process.INVALID_UID) {
+ sj.add("uid=" + mRequestParcel.uid);
+ }
+ if (mRequestParcel.packageName != null) {
+ sj.add("packageName=" + mRequestParcel.packageName);
+ }
+ return sj.toString();
}
@Override
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index a0604f2..ebc9e4e 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -69,8 +69,8 @@
import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
import com.android.modules.utils.build.SdkLevel;
-import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.IIpv4PrefixRequest;
+import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.RoutingCoordinatorManager;
import com.android.net.module.util.SharedLog;
@@ -168,10 +168,10 @@
/**
* Request Tethering change.
*
- * @param tetheringType the downstream type of this IpServer.
+ * @param request the TetheringRequest this IpServer was enabled with.
* @param enabled enable or disable tethering.
*/
- public void requestEnableTethering(int tetheringType, boolean enabled) { }
+ public void requestEnableTethering(TetheringRequest request, boolean enabled) { }
}
/** Capture IpServer dependencies, for injection. */
@@ -293,6 +293,9 @@
private LinkAddress mIpv4Address;
+ @Nullable
+ private TetheringRequest mTetheringRequest;
+
private final TetheringMetrics mTetheringMetrics;
private final Handler mHandler;
@@ -406,6 +409,12 @@
return mIpv4PrefixRequest;
}
+ /** The TetheringRequest the IpServer started with. */
+ @Nullable
+ public TetheringRequest getTetheringRequest() {
+ return mTetheringRequest;
+ }
+
/**
* Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper
* thread.
@@ -1033,6 +1042,7 @@
switch (message.what) {
case CMD_TETHER_REQUESTED:
mLastError = TETHER_ERROR_NO_ERROR;
+ mTetheringRequest = (TetheringRequest) message.obj;
switch (message.arg1) {
case STATE_LOCAL_ONLY:
maybeConfigureStaticIp((TetheringRequest) message.obj);
@@ -1168,8 +1178,8 @@
handleNewPrefixRequest((IpPrefix) message.obj);
break;
case CMD_NOTIFY_PREFIX_CONFLICT:
- mLog.i("restart tethering: " + mInterfaceType);
- mCallback.requestEnableTethering(mInterfaceType, false /* enabled */);
+ mLog.i("restart tethering: " + mIfaceName);
+ mCallback.requestEnableTethering(mTetheringRequest, false /* enabled */);
transitionTo(mWaitingForRestartState);
break;
case CMD_SERVICE_FAILED_TO_START:
@@ -1453,12 +1463,12 @@
case CMD_TETHER_UNREQUESTED:
transitionTo(mInitialState);
mLog.i("Untethered (unrequested) and restarting " + mIfaceName);
- mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);
+ mCallback.requestEnableTethering(mTetheringRequest, true /* enabled */);
break;
case CMD_INTERFACE_DOWN:
transitionTo(mUnavailableState);
mLog.i("Untethered (interface down) and restarting " + mIfaceName);
- mCallback.requestEnableTethering(mInterfaceType, true /* enabled */);
+ mCallback.requestEnableTethering(mTetheringRequest, true /* enabled */);
break;
default:
return false;
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 61833c2..f33ef37 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -109,6 +109,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -216,9 +217,11 @@
* Cookie added when registering {@link android.net.TetheringManager.TetheringEventCallback}.
*/
private static class CallbackCookie {
+ public final int uid;
public final boolean hasSystemPrivilege;
- private CallbackCookie(boolean hasSystemPrivilege) {
+ private CallbackCookie(int uid, boolean hasSystemPrivilege) {
+ this.uid = uid;
this.hasSystemPrivilege = hasSystemPrivilege;
}
}
@@ -1116,7 +1119,9 @@
}
/**
- * Builds a TetherStatesParcel for the specified CallbackCookie.
+ * Builds a TetherStatesParcel for the specified CallbackCookie. SoftApConfiguration will only
+ * be included if the cookie has the same uid as the app that specified the configuration, or
+ * if the cookie has system privilege.
*
* @param cookie CallbackCookie of the receiving app.
* @return TetherStatesParcel with information redacted for the specified cookie.
@@ -1132,7 +1137,11 @@
final TetherState tetherState = mTetherStates.valueAt(i);
final int type = tetherState.ipServer.interfaceType();
final String iface = mTetherStates.keyAt(i);
- final TetheringInterface tetheringIface = new TetheringInterface(type, iface);
+ final TetheringRequest request = tetherState.ipServer.getTetheringRequest();
+ final boolean includeSoftApConfig = request != null && cookie != null
+ && (cookie.uid == request.getUid() || cookie.hasSystemPrivilege);
+ final TetheringInterface tetheringIface = new TetheringInterface(type, iface,
+ includeSoftApConfig ? request.getSoftApConfiguration() : null);
if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
errored.add(tetheringIface);
lastErrors.add(tetherState.lastError);
@@ -1170,7 +1179,7 @@
final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED);
bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- TetherStatesParcel parcel = buildTetherStatesParcel(null);
+ TetherStatesParcel parcel = buildTetherStatesParcel(null /* cookie */);
bcast.putStringArrayListExtra(
EXTRA_AVAILABLE_TETHER, toIfaces(Arrays.asList(parcel.availableList)));
bcast.putStringArrayListExtra(
@@ -2194,9 +2203,9 @@
break;
}
case EVENT_REQUEST_CHANGE_DOWNSTREAM: {
- final int tetheringType = message.arg1;
- final Boolean enabled = (Boolean) message.obj;
- enableTetheringInternal(tetheringType, enabled, null);
+ final boolean enabled = message.arg1 == 1;
+ final TetheringRequest request = (TetheringRequest) message.obj;
+ enableTetheringInternal(request.getTetheringType(), enabled, null);
break;
}
default:
@@ -2393,11 +2402,12 @@
/** Register tethering event callback */
void registerTetheringEventCallback(ITetheringEventCallback callback) {
+ final int uid = mDeps.getBinderCallingUid();
final boolean hasSystemPrivilege = hasCallingPermission(NETWORK_SETTINGS)
|| hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)
|| hasCallingPermission(NETWORK_STACK);
mHandler.post(() -> {
- CallbackCookie cookie = new CallbackCookie(hasSystemPrivilege);
+ CallbackCookie cookie = new CallbackCookie(uid, hasSystemPrivilege);
mTetheringEventCallbacks.register(callback, cookie);
final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel();
parcel.supportedTypes = mSupportedTypeBitmap;
@@ -2492,8 +2502,8 @@
if (DBG) {
// Use a CallbackCookie with system privilege so nothing is redacted.
- TetherStatesParcel parcel =
- buildTetherStatesParcel(new CallbackCookie(true /* hasSystemPrivilege */));
+ TetherStatesParcel parcel = buildTetherStatesParcel(
+ new CallbackCookie(Process.SYSTEM_UID, true /* hasSystemPrivilege */));
Log.d(TAG, String.format(
"sendTetherStatesChangedCallback %s=[%s] %s=[%s] %s=[%s] %s=[%s]",
"avail", TextUtils.join(",", Arrays.asList(parcel.availableList)),
@@ -2773,9 +2783,9 @@
}
@Override
- public void requestEnableTethering(int tetheringType, boolean enabled) {
+ public void requestEnableTethering(TetheringRequest request, boolean enabled) {
mTetherMainSM.sendMessage(TetherMainSM.EVENT_REQUEST_CHANGE_DOWNSTREAM,
- tetheringType, 0, enabled ? Boolean.TRUE : Boolean.FALSE);
+ enabled ? 1 : 0, 0, request);
}
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index a4823ca..d89bf4d 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -25,6 +25,7 @@
import android.net.INetd;
import android.net.connectivity.ConnectivityInternalApiUtil;
import android.net.ip.IpServer;
+import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -36,7 +37,6 @@
import androidx.annotation.RequiresApi;
import com.android.modules.utils.build.SdkLevel;
-import com.android.net.module.util.PrivateAddressCoordinator;
import com.android.net.module.util.RoutingCoordinatorManager;
import com.android.net.module.util.RoutingCoordinatorService;
import com.android.net.module.util.SharedLog;
@@ -201,4 +201,11 @@
public WearableConnectionManager makeWearableConnectionManager(Context ctx) {
return new WearableConnectionManager(ctx);
}
+
+ /**
+ * Wrapper to get the binder calling uid for unit testing.
+ */
+ public int getBinderCallingUid() {
+ return Binder.getCallingUid();
+ }
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
index a0198cc..a29f0c2 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringNotificationUpdater.java
@@ -85,7 +85,6 @@
static final int ROAMING_NOTIFICATION_ID = 1003;
@VisibleForTesting
static final int NO_ICON_ID = 0;
- @VisibleForTesting
static final int DOWNSTREAM_NONE = 0;
// Refer to TelephonyManager#getSimCarrierId for more details about carrier id.
@VisibleForTesting
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 d0c036f..17f5081 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -17,7 +17,10 @@
package com.android.networkstack.tethering;
import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.NETWORK_STACK;
import static android.content.pm.PackageManager.GET_ACTIVITIES;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+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;
@@ -33,6 +36,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
@@ -164,6 +168,7 @@
import android.net.wifi.WifiClient;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.SoftApCallback;
+import android.net.wifi.WifiSsid;
import android.net.wifi.p2p.WifiP2pGroup;
import android.net.wifi.p2p.WifiP2pInfo;
import android.net.wifi.p2p.WifiP2pManager;
@@ -190,6 +195,7 @@
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.PrivateAddressCoordinator;
@@ -223,6 +229,7 @@
import java.io.PrintWriter;
import java.net.Inet4Address;
import java.net.Inet6Address;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -257,6 +264,7 @@
private static final String TEST_WIFI_REGEX = "test_wlan\\d";
private static final String TEST_P2P_REGEX = "test_p2p-p2p\\d-.*";
private static final String TEST_BT_REGEX = "test_pan\\d";
+ private static final int TEST_CALLER_UID = 1000;
private static final String TEST_CALLER_PKG = "com.test.tethering";
private static final int CELLULAR_NETID = 100;
@@ -328,6 +336,7 @@
private TestConnectivityManager mCm;
private boolean mForceEthernetServiceUnavailable = false;
+ private int mBinderCallingUid = TEST_CALLER_UID;
private class TestContext extends BroadcastInterceptingContext {
TestContext(Context base) {
@@ -555,6 +564,11 @@
}
return mBluetoothPanShim;
}
+
+ @Override
+ public int getBinderCallingUid() {
+ return mBinderCallingUid;
+ }
}
private static LinkProperties buildUpstreamLinkProperties(String interfaceName,
@@ -2335,7 +2349,7 @@
// 2. Enable wifi tethering.
UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
initTetheringUpstream(upstreamState);
- when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
+ when(mWifiManager.startTetheredHotspot(null)).thenReturn(true);
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
mLooper.dispatchAll();
tetherState = callback.pollTetherStatesChanged();
@@ -2380,6 +2394,105 @@
}
@Test
+ public void testSoftApConfigInTetheringEventCallback() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastV());
+ when(mContext.checkCallingOrSelfPermission(NETWORK_SETTINGS))
+ .thenReturn(PERMISSION_DENIED);
+ when(mContext.checkCallingOrSelfPermission(NETWORK_STACK))
+ .thenReturn(PERMISSION_DENIED);
+ when(mContext.checkCallingOrSelfPermission(PERMISSION_MAINLINE_NETWORK_STACK))
+ .thenReturn(PERMISSION_DENIED);
+ initTetheringOnTestThread();
+ TestTetheringEventCallback callback = new TestTetheringEventCallback();
+ TestTetheringEventCallback differentCallback = new TestTetheringEventCallback();
+ TestTetheringEventCallback settingsCallback = new TestTetheringEventCallback();
+ SoftApConfiguration softApConfig = new SoftApConfiguration.Builder().setWifiSsid(
+ WifiSsid.fromBytes("SoftApConfig".getBytes(StandardCharsets.UTF_8))).build();
+ final TetheringRequest tetheringRequest = new TetheringRequest.Builder(TETHERING_WIFI)
+ .setSoftApConfiguration(softApConfig).build();
+ tetheringRequest.setUid(TEST_CALLER_UID);
+ final TetheringInterface wifiIfaceWithoutConfig = new TetheringInterface(
+ TETHERING_WIFI, TEST_WLAN_IFNAME, null);
+ final TetheringInterface wifiIfaceWithConfig = new TetheringInterface(
+ TETHERING_WIFI, TEST_WLAN_IFNAME, softApConfig);
+
+ // Register callback before running any tethering.
+ mTethering.registerTetheringEventCallback(callback);
+ mLooper.dispatchAll();
+ callback.expectTetheredClientChanged(Collections.emptyList());
+ callback.expectUpstreamChanged(NULL_NETWORK);
+ callback.expectConfigurationChanged(
+ mTethering.getTetheringConfiguration().toStableParcelable());
+ // Register callback with different UID
+ mBinderCallingUid = TEST_CALLER_UID + 1;
+ mTethering.registerTetheringEventCallback(differentCallback);
+ mLooper.dispatchAll();
+ differentCallback.expectTetheredClientChanged(Collections.emptyList());
+ differentCallback.expectUpstreamChanged(NULL_NETWORK);
+ differentCallback.expectConfigurationChanged(
+ mTethering.getTetheringConfiguration().toStableParcelable());
+ // Register Settings callback
+ when(mContext.checkCallingOrSelfPermission(NETWORK_SETTINGS))
+ .thenReturn(PERMISSION_GRANTED);
+ mTethering.registerTetheringEventCallback(settingsCallback);
+ mLooper.dispatchAll();
+ settingsCallback.expectTetheredClientChanged(Collections.emptyList());
+ settingsCallback.expectUpstreamChanged(NULL_NETWORK);
+ settingsCallback.expectConfigurationChanged(
+ mTethering.getTetheringConfiguration().toStableParcelable());
+
+ assertTetherStatesNotNullButEmpty(callback.pollTetherStatesChanged());
+ assertTetherStatesNotNullButEmpty(differentCallback.pollTetherStatesChanged());
+ assertTetherStatesNotNullButEmpty(settingsCallback.pollTetherStatesChanged());
+ callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
+ UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
+ initTetheringUpstream(upstreamState);
+ when(mWifiManager.startTetheredHotspot(null)).thenReturn(true);
+ mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
+ mLooper.dispatchAll();
+ assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
+ callback.pollTetherStatesChanged().availableList);
+ assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
+ differentCallback.pollTetherStatesChanged().availableList);
+ assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
+ settingsCallback.pollTetherStatesChanged().availableList);
+
+ // Enable wifi tethering
+ mBinderCallingUid = TEST_CALLER_UID;
+ mTethering.startTethering(tetheringRequest, TEST_CALLER_PKG, null);
+ sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
+ assertArrayEquals(new TetheringInterface[] {wifiIfaceWithConfig},
+ callback.pollTetherStatesChanged().tetheredList);
+ assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
+ differentCallback.pollTetherStatesChanged().tetheredList);
+ assertArrayEquals(new TetheringInterface[] {wifiIfaceWithConfig},
+ settingsCallback.pollTetherStatesChanged().tetheredList);
+ callback.expectUpstreamChanged(upstreamState.network);
+ callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STARTED);
+
+ // Disable wifi tethering
+ mLooper.dispatchAll();
+ mTethering.stopTethering(TETHERING_WIFI);
+ sendWifiApStateChanged(WIFI_AP_STATE_DISABLED);
+ if (isAtLeastT()) {
+ // After T, tethering doesn't support WIFI_AP_STATE_DISABLED with null interface name.
+ callback.assertNoStateChangeCallback();
+ sendWifiApStateChanged(WIFI_AP_STATE_DISABLED, TEST_WLAN_IFNAME,
+ IFACE_IP_MODE_TETHERED);
+ }
+ assertArrayEquals(new TetheringInterface[] {wifiIfaceWithConfig},
+ callback.pollTetherStatesChanged().availableList);
+ assertArrayEquals(new TetheringInterface[] {wifiIfaceWithoutConfig},
+ differentCallback.pollTetherStatesChanged().availableList);
+ assertArrayEquals(new TetheringInterface[] {wifiIfaceWithConfig},
+ settingsCallback.pollTetherStatesChanged().availableList);
+ mLooper.dispatchAll();
+ callback.expectUpstreamChanged(NULL_NETWORK);
+ callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
+ callback.assertNoCallback();
+ }
+
+ @Test
public void testReportFailCallbackIfOffloadNotSupported() throws Exception {
initTetheringOnTestThread();
final UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
diff --git a/tests/cts/hostside/aidl/Android.bp b/tests/cts/hostside/aidl/Android.bp
index 31924f0..ca3e77f 100644
--- a/tests/cts/hostside/aidl/Android.bp
+++ b/tests/cts/hostside/aidl/Android.bp
@@ -19,8 +19,7 @@
java_test_helper_library {
name: "CtsHostsideNetworkTestsAidl",
- sdk_version: "current",
- min_sdk_version: "30",
+ sdk_version: "system_current",
srcs: [
"com/android/cts/net/hostside/*.aidl",
"com/android/cts/net/hostside/*.java",
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/ITetheringHelper.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/ITetheringHelper.aidl
new file mode 100644
index 0000000..a9f5ed4
--- /dev/null
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/ITetheringHelper.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import android.net.TetheringInterface;
+
+interface ITetheringHelper {
+ TetheringInterface getTetheredWifiInterface();
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/TetheringHelperClient.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/TetheringHelperClient.java
new file mode 100644
index 0000000..5f5ebb0
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/TetheringHelperClient.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.TetheringInterface;
+import android.os.ConditionVariable;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+public class TetheringHelperClient {
+ private static final int TIMEOUT_MS = 5000;
+ private static final String PACKAGE = TetheringHelperClient.class.getPackage().getName();
+ private static final String APP2_PACKAGE = PACKAGE + ".app2";
+ private static final String SERVICE_NAME = APP2_PACKAGE + ".TetheringHelperService";
+
+ private Context mContext;
+ private ServiceConnection mServiceConnection;
+ private ITetheringHelper mService;
+
+ public TetheringHelperClient(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Binds to TetheringHelperService.
+ */
+ public void bind() {
+ if (mService != null) {
+ throw new IllegalStateException("Already bound");
+ }
+
+ final ConditionVariable cv = new ConditionVariable();
+ mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = ITetheringHelper.Stub.asInterface(service);
+ cv.open();
+ }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ }
+ };
+
+ final Intent intent = new Intent();
+ intent.setComponent(new ComponentName(APP2_PACKAGE, SERVICE_NAME));
+ mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ cv.block(TIMEOUT_MS);
+ if (mService == null) {
+ throw new IllegalStateException(
+ "Could not bind to TetheringHelperService after " + TIMEOUT_MS + "ms");
+ }
+ }
+
+ /**
+ * Unbinds from TetheringHelperService.
+ */
+ public void unbind() {
+ if (mService != null) {
+ mContext.unbindService(mServiceConnection);
+ }
+ }
+
+ /**
+ * Returns the tethered Wifi interface as seen from TetheringHelperService.
+ */
+ public TetheringInterface getTetheredWifiInterface() throws RemoteException {
+ return mService.getTetheredWifiInterface();
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/TetheringTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/TetheringTest.java
new file mode 100644
index 0000000..ad98a29
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/TetheringTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.content.Context;
+import android.net.TetheringInterface;
+import android.net.cts.util.CtsTetheringUtils;
+import android.net.wifi.SoftApConfiguration;
+import android.net.wifi.WifiSsid;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.charset.StandardCharsets;
+
+public class TetheringTest {
+ private CtsTetheringUtils mCtsTetheringUtils;
+ private TetheringHelperClient mTetheringHelperClient;
+
+ @Before
+ public void setUp() throws Exception {
+ Context targetContext = getInstrumentation().getTargetContext();
+ mCtsTetheringUtils = new CtsTetheringUtils(targetContext);
+ mTetheringHelperClient = new TetheringHelperClient(targetContext);
+ mTetheringHelperClient.bind();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mTetheringHelperClient.unbind();
+ }
+
+ /**
+ * Starts Wifi tethering and tests that the SoftApConfiguration is redacted from
+ * TetheringEventCallback for other apps.
+ */
+ @Test
+ public void testSoftApConfigurationRedactedForOtherUids() throws Exception {
+ final CtsTetheringUtils.TestTetheringEventCallback tetherEventCallback =
+ mCtsTetheringUtils.registerTetheringEventCallback();
+ SoftApConfiguration softApConfig = new SoftApConfiguration.Builder()
+ .setWifiSsid(WifiSsid.fromBytes("This is an SSID!"
+ .getBytes(StandardCharsets.UTF_8))).build();
+ final TetheringInterface tetheringInterface =
+ mCtsTetheringUtils.startWifiTethering(tetherEventCallback, softApConfig);
+ assertNotNull(tetheringInterface);
+ assertEquals(softApConfig, tetheringInterface.getSoftApConfiguration());
+ try {
+ TetheringInterface tetheringInterfaceForApp2 =
+ mTetheringHelperClient.getTetheredWifiInterface();
+ assertNotNull(tetheringInterfaceForApp2);
+ assertNull(tetheringInterfaceForApp2.getSoftApConfiguration());
+ assertEquals(
+ tetheringInterface.getInterface(), tetheringInterfaceForApp2.getInterface());
+ } finally {
+ mCtsTetheringUtils.stopWifiTethering(tetherEventCallback);
+ }
+ }
+}
diff --git a/tests/cts/hostside/app2/AndroidManifest.xml b/tests/cts/hostside/app2/AndroidManifest.xml
index 412b307..6ccaf4f 100644
--- a/tests/cts/hostside/app2/AndroidManifest.xml
+++ b/tests/cts/hostside/app2/AndroidManifest.xml
@@ -42,6 +42,8 @@
android:debuggable="true">
<service android:name=".RemoteSocketFactoryService"
android:exported="true"/>
+ <service android:name=".TetheringHelperService"
+ android:exported="true"/>
</application>
<!--
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/TetheringHelperService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/TetheringHelperService.java
new file mode 100644
index 0000000..56a8cbb
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/TetheringHelperService.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside.app2;
+
+
+import static android.net.TetheringManager.TETHERING_WIFI;
+
+import android.app.Service;
+import android.content.Intent;
+import android.net.TetheringInterface;
+import android.net.TetheringManager;
+import android.os.IBinder;
+
+import androidx.annotation.NonNull;
+
+import com.android.cts.net.hostside.ITetheringHelper;
+
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class TetheringHelperService extends Service {
+ private static final String TAG = TetheringHelperService.class.getSimpleName();
+
+ private ITetheringHelper.Stub mBinder = new ITetheringHelper.Stub() {
+ public TetheringInterface getTetheredWifiInterface() {
+ ArrayBlockingQueue<TetheringInterface> queue = new ArrayBlockingQueue<>(1);
+ TetheringManager.TetheringEventCallback callback =
+ new TetheringManager.TetheringEventCallback() {
+ @Override
+ public void onTetheredInterfacesChanged(
+ @NonNull Set<TetheringInterface> interfaces) {
+ for (TetheringInterface iface : interfaces) {
+ if (iface.getType() == TETHERING_WIFI) {
+ queue.offer(iface);
+ break;
+ }
+ }
+ }
+ };
+ TetheringManager tm =
+ TetheringHelperService.this.getSystemService(TetheringManager.class);
+ tm.registerTetheringEventCallback(Runnable::run, callback);
+ TetheringInterface iface;
+ try {
+ iface = queue.poll(5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new IllegalStateException("Wait for wifi TetheredInterface interrupted");
+ } finally {
+ tm.unregisterTetheringEventCallback(callback);
+ }
+ if (iface == null) {
+ throw new IllegalStateException(
+ "No wifi TetheredInterface received after 5 seconds");
+ }
+ return iface;
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideTetheringTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideTetheringTests.java
new file mode 100644
index 0000000..d73e01a
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideTetheringTests.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
+import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class HostsideTetheringTests extends HostsideNetworkTestCase {
+ /**
+ * Set up the test once before running all the tests.
+ */
+ @BeforeClassWithInfo
+ public static void setUpOnce(TestInformation testInfo) throws Exception {
+ uninstallPackage(testInfo, TEST_APP2_PKG, false);
+ installPackage(testInfo, TEST_APP2_APK);
+ }
+
+ /**
+ * Tear down the test once after running all the tests.
+ */
+ @AfterClassWithInfo
+ public static void tearDownOnce(TestInformation testInfo)
+ throws DeviceNotAvailableException {
+ uninstallPackage(testInfo, TEST_APP2_PKG, true);
+ }
+
+ @Test
+ public void testSoftApConfigurationRedactedForOtherApps() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".TetheringTest",
+ "testSoftApConfigurationRedactedForOtherUids");
+ }
+}
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
index dffd9d5..243cd27 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
@@ -46,6 +46,7 @@
import android.net.TetheringManager.TetheringEventCallback;
import android.net.TetheringManager.TetheringInterfaceRegexps;
import android.net.TetheringManager.TetheringRequest;
+import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiClient;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.SoftApCallback;
@@ -491,13 +492,29 @@
}
}
+ /**
+ * Starts Wi-Fi tethering.
+ */
public TetheringInterface startWifiTethering(final TestTetheringEventCallback callback)
throws InterruptedException {
+ return startWifiTethering(callback, null);
+ }
+
+ /**
+ * Starts Wi-Fi tethering with the specified SoftApConfiguration.
+ */
+ public TetheringInterface startWifiTethering(final TestTetheringEventCallback callback,
+ final SoftApConfiguration softApConfiguration)
+ throws InterruptedException {
final List<String> wifiRegexs = getWifiTetherableInterfaceRegexps(callback);
final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
- final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
- .setShouldShowEntitlementUi(false).build();
+ TetheringRequest.Builder builder = new TetheringRequest.Builder(TETHERING_WIFI)
+ .setShouldShowEntitlementUi(false);
+ if (softApConfiguration != null) {
+ builder.setSoftApConfiguration(softApConfiguration);
+ }
+ final TetheringRequest request = builder.build();
return runAsShell(TETHER_PRIVILEGED, () -> {
mTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback);
diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
index a07c9ea..47d444f 100644
--- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -22,6 +22,8 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
+import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_ETHERNET;
import static android.net.TetheringManager.TETHERING_NCM;
@@ -244,9 +246,16 @@
assertNull(tr.getClientStaticIpv4Address());
assertFalse(tr.isExemptFromEntitlementCheck());
assertTrue(tr.getShouldShowEntitlementUi());
+ assertEquals(CONNECTIVITY_SCOPE_GLOBAL, tr.getConnectivityScope());
assertEquals(softApConfiguration, tr.getSoftApConfiguration());
assertEquals(INVALID_UID, tr.getUid());
assertNull(tr.getPackageName());
+ assertEquals(tr.toString(), "TetheringRequest[ "
+ + "TETHERING_WIFI, "
+ + "showProvisioningUi, "
+ + "CONNECTIVITY_SCOPE_GLOBAL, "
+ + "softApConfig=" + softApConfiguration.toString()
+ + " ]");
final LinkAddress localAddr = new LinkAddress("192.168.24.5/24");
final LinkAddress clientAddr = new LinkAddress("192.168.24.100/24");
@@ -254,6 +263,7 @@
.setStaticIpv4Addresses(localAddr, clientAddr)
.setExemptFromEntitlementCheck(true)
.setShouldShowEntitlementUi(false)
+ .setConnectivityScope(CONNECTIVITY_SCOPE_LOCAL)
.build();
int uid = 1000;
String packageName = "package";
@@ -265,13 +275,26 @@
assertEquals(TETHERING_USB, tr2.getTetheringType());
assertTrue(tr2.isExemptFromEntitlementCheck());
assertFalse(tr2.getShouldShowEntitlementUi());
+ assertEquals(CONNECTIVITY_SCOPE_LOCAL, tr2.getConnectivityScope());
+ assertNull(tr2.getSoftApConfiguration());
assertEquals(uid, tr2.getUid());
assertEquals(packageName, tr2.getPackageName());
+ assertEquals(tr2.toString(), "TetheringRequest[ "
+ + "TETHERING_USB, "
+ + "localIpv4Address=" + localAddr + ", "
+ + "staticClientAddress=" + clientAddr + ", "
+ + "exemptFromEntitlementCheck, "
+ + "CONNECTIVITY_SCOPE_LOCAL, "
+ + "uid=1000, "
+ + "packageName=package"
+ + " ]");
final TetheringRequest tr3 = new TetheringRequest.Builder(TETHERING_USB)
.setStaticIpv4Addresses(localAddr, clientAddr)
.setExemptFromEntitlementCheck(true)
- .setShouldShowEntitlementUi(false).build();
+ .setShouldShowEntitlementUi(false)
+ .setConnectivityScope(CONNECTIVITY_SCOPE_LOCAL)
+ .build();
tr3.setUid(uid);
tr3.setPackageName(packageName);
assertEquals(tr2, tr3);
@@ -340,10 +363,14 @@
tetherEventCallback.assumeWifiTetheringSupported(mContext);
tetherEventCallback.expectNoTetheringActive();
+ SoftApConfiguration softApConfig = new SoftApConfiguration.Builder()
+ .setWifiSsid(WifiSsid.fromBytes("This is an SSID!"
+ .getBytes(StandardCharsets.UTF_8))).build();
final TetheringInterface tetheredIface =
- mCtsTetheringUtils.startWifiTethering(tetherEventCallback);
+ mCtsTetheringUtils.startWifiTethering(tetherEventCallback, softApConfig);
assertNotNull(tetheredIface);
+ assertEquals(softApConfig, tetheredIface.getSoftApConfiguration());
final String wifiTetheringIface = tetheredIface.getInterface();
mCtsTetheringUtils.stopWifiTethering(tetherEventCallback);