diff --git a/tests/cts/hostside/AndroidTest.xml b/tests/cts/hostside/AndroidTest.xml
index 7cc0dd19cc..b7fefaf3b5 100644
--- a/tests/cts/hostside/AndroidTest.xml
+++ b/tests/cts/hostside/AndroidTest.xml
@@ -20,6 +20,7 @@
+
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 21212af824..29ba68c4cf 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -126,12 +126,10 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase {
protected Context mContext;
protected Instrumentation mInstrumentation;
protected ConnectivityManager mCm;
- protected WifiManager mWfm;
protected int mUid;
private int mMyUid;
private MyServiceClient mServiceClient;
private String mDeviceIdleConstantsSetting;
- private boolean mIsLocationOn;
@Rule
public final RuleChain mRuleChain = RuleChain.outerRule(new RequiredPropertiesRule())
@@ -144,16 +142,11 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase {
mInstrumentation = getInstrumentation();
mContext = getContext();
mCm = getConnectivityManager();
- mWfm = getWifiManager();
mUid = getUid(TEST_APP2_PKG);
mMyUid = getUid(mContext.getPackageName());
mServiceClient = new MyServiceClient(mContext);
mServiceClient.bind();
mDeviceIdleConstantsSetting = "device_idle_constants";
- mIsLocationOn = isLocationOn();
- if (!mIsLocationOn) {
- enableLocation();
- }
executeShellCommand("cmd netpolicy start-watching " + mUid);
setAppIdle(false);
@@ -164,33 +157,9 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase {
protected void tearDown() throws Exception {
executeShellCommand("cmd netpolicy stop-watching");
- if (!mIsLocationOn) {
- disableLocation();
- }
mServiceClient.unbind();
}
- private void enableLocation() throws Exception {
- Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.LOCATION_MODE,
- Settings.Secure.LOCATION_MODE_SENSORS_ONLY);
- assertEquals(Settings.Secure.LOCATION_MODE_SENSORS_ONLY,
- Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.LOCATION_MODE));
- }
-
- private void disableLocation() throws Exception {
- Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.LOCATION_MODE,
- Settings.Secure.LOCATION_MODE_OFF);
- assertEquals(Settings.Secure.LOCATION_MODE_OFF,
- Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.LOCATION_MODE));
- }
-
- private boolean isLocationOn() throws Exception {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.LOCATION_MODE) != Settings.Secure.LOCATION_MODE_OFF;
- }
-
protected int getUid(String packageName) throws Exception {
return mContext.getPackageManager().getPackageUid(packageName, 0);
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
index ca2864c0b8..3807d79c35 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
@@ -27,17 +27,20 @@ import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTest
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.content.Context;
+import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.wifi.WifiManager;
+import android.os.Process;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -113,6 +116,20 @@ public class NetworkPolicyTestUtils {
return am.isLowRamDevice();
}
+ public static boolean isLocationEnabled() {
+ final LocationManager lm = (LocationManager) getContext().getSystemService(
+ Context.LOCATION_SERVICE);
+ return lm.isLocationEnabled();
+ }
+
+ public static void setLocationEnabled(boolean enabled) {
+ final LocationManager lm = (LocationManager) getContext().getSystemService(
+ Context.LOCATION_SERVICE);
+ lm.setLocationEnabledForUser(enabled, Process.myUserHandle());
+ assertEquals("Couldn't change location enabled state", lm.isLocationEnabled(), enabled);
+ Log.d(TAG, "Changed location enabled state to " + enabled);
+ }
+
public static boolean isActiveNetworkMetered(boolean metered) {
return getConnectivityManager().isActiveNetworkMetered() == metered;
}
@@ -128,9 +145,21 @@ public class NetworkPolicyTestUtils {
if (isActiveNetworkMetered(metered)) {
return null;
}
- final String ssid = unquoteSSID(getWifiManager().getConnectionInfo().getSSID());
- setWifiMeteredStatus(ssid, metered);
- return Pair.create(ssid, !metered);
+ final boolean isLocationEnabled = isLocationEnabled();
+ try {
+ if (!isLocationEnabled) {
+ setLocationEnabled(true);
+ }
+ final String ssid = unquoteSSID(getWifiManager().getConnectionInfo().getSSID());
+ assertNotEquals(WifiManager.UNKNOWN_SSID, ssid);
+ setWifiMeteredStatus(ssid, metered);
+ return Pair.create(ssid, !metered);
+ } finally {
+ // Reset the location enabled state
+ if (!isLocationEnabled) {
+ setLocationEnabled(false);
+ }
+ }
}
public static void resetMeteredNetwork(String ssid, boolean metered) throws Exception {
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 85c94e7db2..2fdd5fb201 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -24,6 +24,7 @@ import android.net.NetworkAgentConfig
import android.net.NetworkCapabilities
import android.net.NetworkProvider
import android.net.NetworkRequest
+import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnBandwidthUpdateRequested
import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkUnwanted
import android.os.Build
import android.os.HandlerThread
@@ -88,9 +89,15 @@ class NetworkAgentTest {
private val history = ArrayTrackRecord().newReadHead()
sealed class CallbackEntry {
+ object OnBandwidthUpdateRequested : CallbackEntry()
object OnNetworkUnwanted : CallbackEntry()
}
+ override fun onBandwidthUpdateRequested() {
+ super.onBandwidthUpdateRequested()
+ history.add(OnBandwidthUpdateRequested)
+ }
+
override fun onNetworkUnwanted() {
super.onNetworkUnwanted()
history.add(OnNetworkUnwanted)
@@ -139,4 +146,13 @@ class NetworkAgentTest {
agent.register()
}
}
+
+ @Test
+ fun testOnBandwidthUpdateRequested() {
+ val (agent, callback) = createConnectedNetworkAgent()
+ callback.expectAvailableThenValidatedCallbacks(agent.network)
+ mCM.requestBandwidthUpdate(agent.network)
+ agent.expectCallback()
+ agent.unregister()
+ }
}
diff --git a/tests/cts/tethering/OWNERS b/tests/cts/tethering/OWNERS
new file mode 100644
index 0000000000..cd6abeb6e8
--- /dev/null
+++ b/tests/cts/tethering/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 31808
+lorenzo@google.com
+satk@google.com
+
diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
index 86fe54ce54..193a5dc3a4 100644
--- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -29,9 +29,14 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.LinkAddress;
+import android.net.Network;
+import android.net.TetheredClient;
import android.net.TetheringManager;
+import android.net.TetheringManager.TetheringEventCallback;
+import android.net.TetheringManager.TetheringInterfaceRegexps;
import android.net.TetheringManager.TetheringRequest;
+import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -42,6 +47,8 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -195,8 +202,12 @@ public class TetheringManagerTest {
}
}
- private static boolean isIfaceMatch(final String[] ifaceRegexs,
- final ArrayList ifaces) {
+ private static boolean isIfaceMatch(final List ifaceRegexs,
+ final List ifaces) {
+ return isIfaceMatch(ifaceRegexs.toArray(new String[0]), ifaces);
+ }
+
+ private static boolean isIfaceMatch(final String[] ifaceRegexs, final List ifaces) {
if (ifaceRegexs == null) fail("ifaceRegexs should not be null");
if (ifaces == null) return false;
@@ -251,4 +262,196 @@ public class TetheringManagerTest {
assertTrue(tr2.isExemptFromEntitlementCheck());
assertFalse(tr2.getShouldShowEntitlementUi());
}
+
+ // Must poll the callback before looking at the member.
+ private static class TestTetheringEventCallback implements TetheringEventCallback {
+ public enum CallbackType {
+ ON_SUPPORTED,
+ ON_UPSTREAM,
+ ON_TETHERABLE_REGEX,
+ ON_TETHERABLE_IFACES,
+ ON_TETHERED_IFACES,
+ ON_ERROR,
+ ON_CLIENTS,
+ };
+
+ public static class CallbackValue {
+ public final CallbackType callbackType;
+ public final Object callbackParam;
+ public final int callbackParam2;
+
+ private CallbackValue(final CallbackType type, final Object param, final int param2) {
+ this.callbackType = type;
+ this.callbackParam = param;
+ this.callbackParam2 = param2;
+ }
+ }
+ private final LinkedBlockingQueue mCallbacks = new LinkedBlockingQueue<>();
+
+ private TetheringInterfaceRegexps mTetherableRegex;
+ private List mTetherableIfaces;
+ private List mTetheredIfaces;
+
+ @Override
+ public void onTetheringSupported(boolean supported) {
+ mCallbacks.add(new CallbackValue(CallbackType.ON_SUPPORTED, null, 0));
+ }
+
+ @Override
+ public void onUpstreamChanged(Network network) {
+ mCallbacks.add(new CallbackValue(CallbackType.ON_UPSTREAM, network, 0));
+ }
+
+ @Override
+ public void onTetherableInterfaceRegexpsChanged(TetheringInterfaceRegexps reg) {
+ mTetherableRegex = reg;
+ mCallbacks.add(new CallbackValue(CallbackType.ON_TETHERABLE_REGEX, reg, 0));
+ }
+
+ @Override
+ public void onTetherableInterfacesChanged(List interfaces) {
+ mTetherableIfaces = interfaces;
+ mCallbacks.add(new CallbackValue(CallbackType.ON_TETHERABLE_IFACES, interfaces, 0));
+ }
+
+ @Override
+ public void onTetheredInterfacesChanged(List interfaces) {
+ mTetheredIfaces = interfaces;
+ mCallbacks.add(new CallbackValue(CallbackType.ON_TETHERED_IFACES, interfaces, 0));
+ }
+
+ @Override
+ public void onError(String ifName, int error) {
+ mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, ifName, error));
+ }
+
+ @Override
+ public void onClientsChanged(Collection clients) {
+ mCallbacks.add(new CallbackValue(CallbackType.ON_CLIENTS, clients, 0));
+ }
+
+ public CallbackValue pollCallback() {
+ try {
+ return mCallbacks.poll(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ fail("Callback not seen");
+ }
+ return null;
+ }
+
+ public void expectTetherableInterfacesChanged(@NonNull List regexs) {
+ while (true) {
+ final CallbackValue cv = pollCallback();
+ if (cv == null) fail("No expected tetherable ifaces callback");
+ if (cv.callbackType != CallbackType.ON_TETHERABLE_IFACES) continue;
+
+ final List interfaces = (List) cv.callbackParam;
+ if (isIfaceMatch(regexs, interfaces)) break;
+ }
+ }
+
+ public void expectTetheredInterfacesChanged(@NonNull List regexs) {
+ while (true) {
+ final CallbackValue cv = pollCallback();
+ if (cv == null) fail("No expected tethered ifaces callback");
+ if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) continue;
+
+ final List interfaces = (List) cv.callbackParam;
+
+ // Null regexs means no active tethering.
+ if (regexs == null) {
+ if (interfaces.size() == 0) break;
+ } else if (isIfaceMatch(regexs, interfaces)) {
+ break;
+ }
+ }
+ }
+
+ public void expectCallbackStarted() {
+ // The each bit represent a type from CallbackType.ON_*.
+ // Expect all of callbacks except for ON_ERROR.
+ final int expectedBitMap = 0x7f ^ (1 << CallbackType.ON_ERROR.ordinal());
+ int receivedBitMap = 0;
+ while (receivedBitMap != expectedBitMap) {
+ final CallbackValue cv = pollCallback();
+ if (cv == null) {
+ fail("No expected callbacks, " + "expected bitmap: "
+ + expectedBitMap + ", actual: " + receivedBitMap);
+ }
+ receivedBitMap = receivedBitMap | (1 << cv.callbackType.ordinal());
+ }
+ }
+
+ public TetheringInterfaceRegexps getTetheringInterfaceRegexps() {
+ return mTetherableRegex;
+ }
+
+ public List getTetherableInterfaces() {
+ return mTetherableIfaces;
+ }
+
+ public List getTetheredInterfaces() {
+ return mTetheredIfaces;
+ }
+ }
+
+ @Test
+ public void testRegisterTetheringEventCallback() throws Exception {
+ if (!mTM.isTetheringSupported()) return;
+
+ final TestTetheringEventCallback tetherEventCallback = new TestTetheringEventCallback();
+
+ mTM.registerTetheringEventCallback(c -> c.run(), tetherEventCallback);
+ tetherEventCallback.expectCallbackStarted();
+
+ final TetheringInterfaceRegexps tetherableRegexs =
+ tetherEventCallback.getTetheringInterfaceRegexps();
+ final List wifiRegexs = tetherableRegexs.getTetherableWifiRegexs();
+ if (wifiRegexs.size() == 0) return;
+
+ final boolean isIfaceAvailWhenNoTethering =
+ isIfaceMatch(wifiRegexs, tetherEventCallback.getTetherableInterfaces());
+
+ mTM.startTethering(new TetheringRequest.Builder(TETHERING_WIFI).build(), c -> c.run(),
+ new StartTetheringCallback());
+
+ // If interface is already available before starting tethering, the available callback may
+ // not be sent after tethering enabled.
+ if (!isIfaceAvailWhenNoTethering) {
+ tetherEventCallback.expectTetherableInterfacesChanged(wifiRegexs);
+ }
+
+ tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs);
+
+ mTM.stopTethering(TETHERING_WIFI);
+
+ tetherEventCallback.expectTetheredInterfacesChanged(null);
+ mTM.unregisterTetheringEventCallback(tetherEventCallback);
+ }
+
+ @Test
+ public void testGetTetherableInterfaceRegexps() {
+ if (!mTM.isTetheringSupported()) return;
+
+ final TestTetheringEventCallback tetherEventCallback = new TestTetheringEventCallback();
+ mTM.registerTetheringEventCallback(c -> c.run(), tetherEventCallback);
+ tetherEventCallback.expectCallbackStarted();
+
+ final TetheringInterfaceRegexps tetherableRegexs =
+ tetherEventCallback.getTetheringInterfaceRegexps();
+ final List wifiRegexs = tetherableRegexs.getTetherableWifiRegexs();
+ final List usbRegexs = tetherableRegexs.getTetherableUsbRegexs();
+ final List btRegexs = tetherableRegexs.getTetherableBluetoothRegexs();
+
+ assertEquals(wifiRegexs, Arrays.asList(mTM.getTetherableWifiRegexs()));
+ assertEquals(usbRegexs, Arrays.asList(mTM.getTetherableUsbRegexs()));
+ assertEquals(btRegexs, Arrays.asList(mTM.getTetherableBluetoothRegexs()));
+
+ //Verify that any of interface name should only contain in one array.
+ wifiRegexs.forEach(s -> assertFalse(usbRegexs.contains(s)));
+ wifiRegexs.forEach(s -> assertFalse(btRegexs.contains(s)));
+ usbRegexs.forEach(s -> assertFalse(btRegexs.contains(s)));
+
+ mTM.unregisterTetheringEventCallback(tetherEventCallback);
+ }
}