Test enable tethering permission and stopAllTethering

1. Test whether start tethering is gated by suitable permission.
2. Test stopAllTethering

Bug: 153613718
Test: atest CtsTetheringTest

Change-Id: I38702886ea355e1aec8eb8ac404fdd46a44582e3
This commit is contained in:
markchien
2020-04-11 18:53:35 +08:00
parent 12b24407cf
commit 257cad8052
2 changed files with 189 additions and 86 deletions

View File

@@ -27,6 +27,7 @@ android_test {
static_libs: [ static_libs: [
"TetheringIntegrationTestsLib", "TetheringIntegrationTestsLib",
"compatibility-device-util-axt", "compatibility-device-util-axt",
"net-tests-utils",
"ctstestrunner-axt", "ctstestrunner-axt",
"junit", "junit",
"junit-params", "junit-params",

View File

@@ -15,20 +15,24 @@
*/ */
package android.tethering.test; package android.tethering.test;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHERING_WIFI_P2P; import static android.net.TetheringManager.TETHERING_WIFI_P2P;
import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import android.app.UiAutomation;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -48,6 +52,8 @@ import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4;
import com.android.testutils.ArrayTrackRecord;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -77,11 +83,21 @@ public class TetheringManagerTest {
private static final int DEFAULT_TIMEOUT_MS = 60_000; private static final int DEFAULT_TIMEOUT_MS = 60_000;
private void adoptShellPermissionIdentity() {
final UiAutomation uiAutomation =
InstrumentationRegistry.getInstrumentation().getUiAutomation();
uiAutomation.adoptShellPermissionIdentity();
}
private void dropShellPermissionIdentity() {
final UiAutomation uiAutomation =
InstrumentationRegistry.getInstrumentation().getUiAutomation();
uiAutomation.dropShellPermissionIdentity();
}
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
InstrumentationRegistry.getInstrumentation() adoptShellPermissionIdentity();
.getUiAutomation()
.adoptShellPermissionIdentity();
mContext = InstrumentationRegistry.getContext(); mContext = InstrumentationRegistry.getContext();
mTM = (TetheringManager) mContext.getSystemService(Context.TETHERING_SERVICE); mTM = (TetheringManager) mContext.getSystemService(Context.TETHERING_SERVICE);
mTetherChangeReceiver = new TetherChangeReceiver(); mTetherChangeReceiver = new TetherChangeReceiver();
@@ -93,10 +109,9 @@ public class TetheringManagerTest {
@After @After
public void tearDown() throws Exception { public void tearDown() throws Exception {
mTM.stopAllTethering();
mContext.unregisterReceiver(mTetherChangeReceiver); mContext.unregisterReceiver(mTetherChangeReceiver);
InstrumentationRegistry.getInstrumentation() dropShellPermissionIdentity();
.getUiAutomation()
.dropShellPermissionIdentity();
} }
private class TetherChangeReceiver extends BroadcastReceiver { private class TetherChangeReceiver extends BroadcastReceiver {
@@ -202,15 +217,54 @@ public class TetheringManagerTest {
} }
} }
private class StartTetheringCallback implements TetheringManager.StartTetheringCallback { private static class StartTetheringCallback implements TetheringManager.StartTetheringCallback {
private static int TIMEOUT_MS = 30_000;
public static class CallbackValue {
public final int error;
private CallbackValue(final int e) {
error = e;
}
public static class OnTetheringStarted extends CallbackValue {
OnTetheringStarted() { super(TETHER_ERROR_NO_ERROR); }
}
public static class OnTetheringFailed extends CallbackValue {
OnTetheringFailed(final int error) { super(error); }
}
@Override
public String toString() {
return String.format("%s(%d)", getClass().getSimpleName(), error);
}
}
private final ArrayTrackRecord<CallbackValue>.ReadHead mHistory =
new ArrayTrackRecord<CallbackValue>().newReadHead();
@Override @Override
public void onTetheringStarted() { public void onTetheringStarted() {
// Do nothing, TetherChangeReceiver will wait until it receives the broadcast. mHistory.add(new CallbackValue.OnTetheringStarted());
} }
@Override @Override
public void onTetheringFailed(final int error) { public void onTetheringFailed(final int error) {
fail("startTethering fail: " + error); mHistory.add(new CallbackValue.OnTetheringFailed(error));
}
public void verifyTetheringStarted() {
final CallbackValue cv = mHistory.poll(TIMEOUT_MS, c -> true);
assertNotNull("No onTetheringStarted after " + TIMEOUT_MS + " ms", cv);
assertTrue("Fail start tethering:" + cv,
cv instanceof CallbackValue.OnTetheringStarted);
}
public void expectTetheringFailed(final int expected) throws InterruptedException {
final CallbackValue cv = mHistory.poll(TIMEOUT_MS, c -> true);
assertNotNull("No onTetheringFailed after " + TIMEOUT_MS + " ms", cv);
assertTrue("Expect fail with error code " + expected + ", but received: " + cv,
(cv instanceof CallbackValue.OnTetheringFailed) && (cv.error == expected));
} }
} }
@@ -244,8 +298,10 @@ public class TetheringManagerTest {
mTetherChangeReceiver.expectNoActiveTethering(0 /** timeout */); mTetherChangeReceiver.expectNoActiveTethering(0 /** timeout */);
final StartTetheringCallback startTetheringCallback = new StartTetheringCallback(); final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
mTM.startTethering(new TetheringRequest.Builder(TETHERING_WIFI).build(), c -> c.run(), mTM.startTethering(new TetheringRequest.Builder(TETHERING_WIFI).build(),
startTetheringCallback); c -> c.run() /* executor */, startTetheringCallback);
startTetheringCallback.verifyTetheringStarted();
mTetherChangeReceiver.expectActiveTethering(wifiRegexs); mTetherChangeReceiver.expectActiveTethering(wifiRegexs);
mTM.stopTethering(TETHERING_WIFI); mTM.stopTethering(TETHERING_WIFI);
@@ -277,6 +333,7 @@ public class TetheringManagerTest {
// Must poll the callback before looking at the member. // Must poll the callback before looking at the member.
private static class TestTetheringEventCallback implements TetheringEventCallback { private static class TestTetheringEventCallback implements TetheringEventCallback {
private static final int TIMEOUT_MS = 30_000;
public enum CallbackType { public enum CallbackType {
ON_SUPPORTED, ON_SUPPORTED,
ON_UPSTREAM, ON_UPSTREAM,
@@ -299,7 +356,10 @@ public class TetheringManagerTest {
this.callbackParam2 = param2; this.callbackParam2 = param2;
} }
} }
private final LinkedBlockingQueue<CallbackValue> mCallbacks = new LinkedBlockingQueue<>();
private final ArrayTrackRecord<CallbackValue>.ReadHead mHistory =
new ArrayTrackRecord<CallbackValue>().newReadHead();
private TetheringInterfaceRegexps mTetherableRegex; private TetheringInterfaceRegexps mTetherableRegex;
private List<String> mTetherableIfaces; private List<String> mTetherableIfaces;
@@ -307,108 +367,96 @@ public class TetheringManagerTest {
@Override @Override
public void onTetheringSupported(boolean supported) { public void onTetheringSupported(boolean supported) {
mCallbacks.add(new CallbackValue(CallbackType.ON_SUPPORTED, null, 0)); mHistory.add(new CallbackValue(CallbackType.ON_SUPPORTED, null, 0));
} }
@Override @Override
public void onUpstreamChanged(Network network) { public void onUpstreamChanged(Network network) {
mCallbacks.add(new CallbackValue(CallbackType.ON_UPSTREAM, network, 0)); mHistory.add(new CallbackValue(CallbackType.ON_UPSTREAM, network, 0));
} }
@Override @Override
public void onTetherableInterfaceRegexpsChanged(TetheringInterfaceRegexps reg) { public void onTetherableInterfaceRegexpsChanged(TetheringInterfaceRegexps reg) {
mTetherableRegex = reg; mTetherableRegex = reg;
mCallbacks.add(new CallbackValue(CallbackType.ON_TETHERABLE_REGEX, reg, 0)); mHistory.add(new CallbackValue(CallbackType.ON_TETHERABLE_REGEX, reg, 0));
} }
@Override @Override
public void onTetherableInterfacesChanged(List<String> interfaces) { public void onTetherableInterfacesChanged(List<String> interfaces) {
mTetherableIfaces = interfaces; mTetherableIfaces = interfaces;
mCallbacks.add(new CallbackValue(CallbackType.ON_TETHERABLE_IFACES, interfaces, 0)); mHistory.add(new CallbackValue(CallbackType.ON_TETHERABLE_IFACES, interfaces, 0));
} }
@Override @Override
public void onTetheredInterfacesChanged(List<String> interfaces) { public void onTetheredInterfacesChanged(List<String> interfaces) {
mTetheredIfaces = interfaces; mTetheredIfaces = interfaces;
mCallbacks.add(new CallbackValue(CallbackType.ON_TETHERED_IFACES, interfaces, 0)); mHistory.add(new CallbackValue(CallbackType.ON_TETHERED_IFACES, interfaces, 0));
} }
@Override @Override
public void onError(String ifName, int error) { public void onError(String ifName, int error) {
mCallbacks.add(new CallbackValue(CallbackType.ON_ERROR, ifName, error)); mHistory.add(new CallbackValue(CallbackType.ON_ERROR, ifName, error));
} }
@Override @Override
public void onClientsChanged(Collection<TetheredClient> clients) { public void onClientsChanged(Collection<TetheredClient> clients) {
mCallbacks.add(new CallbackValue(CallbackType.ON_CLIENTS, clients, 0)); mHistory.add(new CallbackValue(CallbackType.ON_CLIENTS, clients, 0));
} }
@Override @Override
public void onOffloadStatusChanged(int status) { public void onOffloadStatusChanged(int status) {
mCallbacks.add(new CallbackValue(CallbackType.ON_OFFLOAD_STATUS, status, 0)); mHistory.add(new CallbackValue(CallbackType.ON_OFFLOAD_STATUS, status, 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<String> regexs) { public void expectTetherableInterfacesChanged(@NonNull List<String> regexs) {
while (true) { assertNotNull("No expected tetherable ifaces callback", mHistory.poll(TIMEOUT_MS,
final CallbackValue cv = pollCallback(); (cv) -> {
if (cv == null) fail("No expected tetherable ifaces callback"); if (cv.callbackType != CallbackType.ON_TETHERABLE_IFACES) return false;
if (cv.callbackType != CallbackType.ON_TETHERABLE_IFACES) continue; final List<String> interfaces = (List<String>) cv.callbackParam;
return isIfaceMatch(regexs, interfaces);
final List<String> interfaces = (List<String>) cv.callbackParam; }));
if (isIfaceMatch(regexs, interfaces)) break;
}
} }
public void expectTetheredInterfacesChanged(@NonNull List<String> regexs) { public void expectTetheredInterfacesChanged(@NonNull List<String> regexs) {
while (true) { assertNotNull("No expected tethered ifaces callback", mHistory.poll(TIMEOUT_MS,
final CallbackValue cv = pollCallback(); (cv) -> {
if (cv == null) fail("No expected tethered ifaces callback"); if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) return false;
if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) continue;
final List<String> interfaces = (List<String>) cv.callbackParam; final List<String> interfaces = (List<String>) cv.callbackParam;
// Null regexs means no active tethering. // Null regexs means no active tethering.
if (regexs == null) { if (regexs == null) return interfaces.isEmpty();
if (interfaces.size() == 0) break;
} else if (isIfaceMatch(regexs, interfaces)) { return isIfaceMatch(regexs, interfaces);
break; }));
}
}
} }
public void expectCallbackStarted() { public void expectCallbackStarted() {
int receivedBitMap = 0;
// The each bit represent a type from CallbackType.ON_*. // The each bit represent a type from CallbackType.ON_*.
// Expect all of callbacks except for ON_ERROR. // Expect all of callbacks except for ON_ERROR.
final int expectedBitMap = 0x7f ^ (1 << CallbackType.ON_ERROR.ordinal()); final int expectedBitMap = 0xff ^ (1 << CallbackType.ON_ERROR.ordinal());
int receivedBitMap = 0; // Receive ON_ERROR on started callback is not matter. It just means tethering is
while (receivedBitMap != expectedBitMap) { // failed last time, should able to continue the test this time.
final CallbackValue cv = pollCallback(); while ((receivedBitMap & expectedBitMap) != expectedBitMap) {
final CallbackValue cv = mHistory.poll(TIMEOUT_MS, c -> true);
if (cv == null) { if (cv == null) {
fail("No expected callbacks, " + "expected bitmap: " fail("No expected callbacks, " + "expected bitmap: "
+ expectedBitMap + ", actual: " + receivedBitMap); + expectedBitMap + ", actual: " + receivedBitMap);
} }
receivedBitMap = receivedBitMap | (1 << cv.callbackType.ordinal()); receivedBitMap |= (1 << cv.callbackType.ordinal());
} }
} }
public void expectOneOfOffloadStatusChanged(int... offloadStatuses) { public void expectOneOfOffloadStatusChanged(int... offloadStatuses) {
while (true) { assertNotNull("No offload status changed", mHistory.poll(TIMEOUT_MS, (cv) -> {
final CallbackValue cv = pollCallback(); if (cv.callbackType != CallbackType.ON_OFFLOAD_STATUS) return false;
if (cv == null) fail("No expected offload status change callback");
if (cv.callbackType != CallbackType.ON_OFFLOAD_STATUS) continue;
final int status = (int) cv.callbackParam; final int status = (int) cv.callbackParam;
for (int offloadStatus : offloadStatuses) if (offloadStatus == status) return; for (int offloadStatus : offloadStatuses) if (offloadStatus == status) return true;
}
return false;
}));
} }
public TetheringInterfaceRegexps getTetheringInterfaceRegexps() { public TetheringInterfaceRegexps getTetheringInterfaceRegexps() {
@@ -424,52 +472,78 @@ public class TetheringManagerTest {
} }
} }
@Test private TestTetheringEventCallback registerTetheringEventCallback() {
public void testRegisterTetheringEventCallback() throws Exception {
if (!mTM.isTetheringSupported()) return;
final TestTetheringEventCallback tetherEventCallback = new TestTetheringEventCallback(); final TestTetheringEventCallback tetherEventCallback = new TestTetheringEventCallback();
mTM.registerTetheringEventCallback(c -> c.run(), tetherEventCallback); mTM.registerTetheringEventCallback(c -> c.run() /* executor */, tetherEventCallback);
tetherEventCallback.expectCallbackStarted(); tetherEventCallback.expectCallbackStarted();
tetherEventCallback.expectOneOfOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
final TetheringInterfaceRegexps tetherableRegexs = return tetherEventCallback;
tetherEventCallback.getTetheringInterfaceRegexps(); }
final List<String> wifiRegexs = tetherableRegexs.getTetherableWifiRegexs();
if (wifiRegexs.size() == 0) return;
private void unregisterTetheringEventCallback(final TestTetheringEventCallback callback) {
mTM.unregisterTetheringEventCallback(callback);
}
private List<String> getWifiTetherableInterfaceRegexps(
final TestTetheringEventCallback callback) {
return callback.getTetheringInterfaceRegexps().getTetherableWifiRegexs();
}
private boolean isWifiTetheringSupported(final TestTetheringEventCallback callback) {
return !getWifiTetherableInterfaceRegexps(callback).isEmpty();
}
private void startWifiTethering(final TestTetheringEventCallback callback)
throws InterruptedException {
final List<String> wifiRegexs = getWifiTetherableInterfaceRegexps(callback);
final boolean isIfaceAvailWhenNoTethering = final boolean isIfaceAvailWhenNoTethering =
isIfaceMatch(wifiRegexs, tetherEventCallback.getTetherableInterfaces()); isIfaceMatch(wifiRegexs, callback.getTetherableInterfaces());
mTM.startTethering(new TetheringRequest.Builder(TETHERING_WIFI).build(), c -> c.run(), final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
new StartTetheringCallback()); mTM.startTethering(new TetheringRequest.Builder(TETHERING_WIFI).build(),
c -> c.run() /* executor */, startTetheringCallback);
startTetheringCallback.verifyTetheringStarted();
// If interface is already available before starting tethering, the available callback may // If interface is already available before starting tethering, the available callback may
// not be sent after tethering enabled. // not be sent after tethering enabled.
if (!isIfaceAvailWhenNoTethering) { if (!isIfaceAvailWhenNoTethering) {
tetherEventCallback.expectTetherableInterfacesChanged(wifiRegexs); callback.expectTetherableInterfacesChanged(wifiRegexs);
} }
tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs); callback.expectTetheredInterfacesChanged(wifiRegexs);
tetherEventCallback.expectOneOfOffloadStatusChanged(
callback.expectOneOfOffloadStatusChanged(
TETHER_HARDWARE_OFFLOAD_STARTED, TETHER_HARDWARE_OFFLOAD_STARTED,
TETHER_HARDWARE_OFFLOAD_FAILED); TETHER_HARDWARE_OFFLOAD_FAILED);
}
private void stopWifiTethering(final TestTetheringEventCallback callback) {
mTM.stopTethering(TETHERING_WIFI); mTM.stopTethering(TETHERING_WIFI);
callback.expectTetheredInterfacesChanged(null);
callback.expectOneOfOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
}
tetherEventCallback.expectTetheredInterfacesChanged(null); @Test
tetherEventCallback.expectOneOfOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); public void testRegisterTetheringEventCallback() throws Exception {
mTM.unregisterTetheringEventCallback(tetherEventCallback); if (!mTM.isTetheringSupported()) return;
final TestTetheringEventCallback tetherEventCallback = registerTetheringEventCallback();
if (!isWifiTetheringSupported(tetherEventCallback)) return;
startWifiTethering(tetherEventCallback);
stopWifiTethering(tetherEventCallback);
unregisterTetheringEventCallback(tetherEventCallback);
} }
@Test @Test
public void testGetTetherableInterfaceRegexps() { public void testGetTetherableInterfaceRegexps() {
if (!mTM.isTetheringSupported()) return; if (!mTM.isTetheringSupported()) return;
final TestTetheringEventCallback tetherEventCallback = new TestTetheringEventCallback(); final TestTetheringEventCallback tetherEventCallback = registerTetheringEventCallback();
mTM.registerTetheringEventCallback(c -> c.run(), tetherEventCallback);
tetherEventCallback.expectCallbackStarted();
final TetheringInterfaceRegexps tetherableRegexs = final TetheringInterfaceRegexps tetherableRegexs =
tetherEventCallback.getTetheringInterfaceRegexps(); tetherEventCallback.getTetheringInterfaceRegexps();
@@ -486,7 +560,35 @@ public class TetheringManagerTest {
wifiRegexs.forEach(s -> assertFalse(btRegexs.contains(s))); wifiRegexs.forEach(s -> assertFalse(btRegexs.contains(s)));
usbRegexs.forEach(s -> assertFalse(btRegexs.contains(s))); usbRegexs.forEach(s -> assertFalse(btRegexs.contains(s)));
mTM.unregisterTetheringEventCallback(tetherEventCallback); unregisterTetheringEventCallback(tetherEventCallback);
}
@Test
public void testStopAllTethering() throws Exception {
if (!mTM.isTetheringSupported()) return;
final TestTetheringEventCallback tetherEventCallback = registerTetheringEventCallback();
if (!isWifiTetheringSupported(tetherEventCallback)) return;
// TODO: start ethernet tethering here when TetheringManagerTest is moved to
// TetheringIntegrationTest.
startWifiTethering(tetherEventCallback);
mTM.stopAllTethering();
tetherEventCallback.expectTetheredInterfacesChanged(null);
unregisterTetheringEventCallback(tetherEventCallback);
}
@Test
public void testEnableTetheringPermission() throws Exception {
dropShellPermissionIdentity();
final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
mTM.startTethering(new TetheringRequest.Builder(TETHERING_WIFI).build(),
c -> c.run() /* executor */, startTetheringCallback);
startTetheringCallback.expectTetheringFailed(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
} }
private class EntitlementResultListener implements OnTetheringEntitlementResultListener { private class EntitlementResultListener implements OnTetheringEntitlementResultListener {