EthernetTetheringTest: obtain the required permissions when needed
Don't adopt all permission in setup, instead obtain permission when
needed. It helps to avoid unexpected permission dropping in functions.
Bug: 237369591
Bug: 242067530
Test: atest EthernetTetheringTest
Change-Id: I1215278271ca6b48e097d7f2cf160416d5411655
diff --git a/Tethering/tests/integration/AndroidManifest.xml b/Tethering/tests/integration/AndroidManifest.xml
index c89c556..9303d0a 100644
--- a/Tethering/tests/integration/AndroidManifest.xml
+++ b/Tethering/tests/integration/AndroidManifest.xml
@@ -16,12 +16,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.networkstack.tethering.tests.integration">
- <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<!-- The test need CHANGE_NETWORK_STATE permission to use requestNetwork API to setup test
network. Since R shell application don't have such permission, grant permission to the test
here. TODO: Remove CHANGE_NETWORK_STATE permission here and use adopt shell perssion to
obtain CHANGE_NETWORK_STATE for testing once R device is no longer supported. -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 82b37f2..c9b3686 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -16,7 +16,6 @@
package android.net;
-import static android.Manifest.permission.ACCESS_NETWORK_STATE;
import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
import static android.Manifest.permission.NETWORK_SETTINGS;
@@ -41,6 +40,7 @@
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
import static com.android.testutils.DeviceInfoUtils.KVersion;
import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
+import static com.android.testutils.TestPermissionUtil.runAsShell;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -189,30 +189,25 @@
@Before
public void setUp() throws Exception {
- // Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive
- // tethered client callbacks. The restricted networks permission is needed to ensure that
- // EthernetManager#isAvailable will correctly return true on devices where Ethernet is
- // marked restricted, like cuttlefish. The dump permission is needed to verify bpf related
- // functions via dumpsys output.
- mUiAutomation.adoptShellPermissionIdentity(
- MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED, ACCESS_NETWORK_STATE,
- DUMP);
mHandlerThread = new HandlerThread(getClass().getSimpleName());
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
- mRunTests = isEthernetTetheringSupported();
+ mRunTests = runAsShell(NETWORK_SETTINGS, TETHER_PRIVILEGED, () ->
+ mTm.isTetheringSupported());
assumeTrue(mRunTests);
mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm);
}
private void cleanUp() throws Exception {
- mTm.setPreferTestNetworks(false);
+ setPreferTestNetworks(false);
if (mUpstreamTracker != null) {
- mUpstreamTracker.teardown();
- mUpstreamTracker = null;
+ runAsShell(MANAGE_TEST_NETWORKS, () -> {
+ mUpstreamTracker.teardown();
+ mUpstreamTracker = null;
+ });
}
if (mUpstreamReader != null) {
TapPacketReader reader = mUpstreamReader;
@@ -220,19 +215,25 @@
mUpstreamReader = null;
}
- mTm.stopTethering(TETHERING_ETHERNET);
- if (mTetheringEventCallback != null) {
- mTetheringEventCallback.awaitInterfaceUntethered();
- mTetheringEventCallback.unregister();
- mTetheringEventCallback = null;
- }
+ runAsShell(TETHER_PRIVILEGED, () -> {
+ mTm.stopTethering(TETHERING_ETHERNET);
+ // Binder call is an async call. Need to hold the shell permission until tethering
+ // stopped. This helps to avoid the test become flaky.
+ if (mTetheringEventCallback != null) {
+ mTetheringEventCallback.awaitInterfaceUntethered();
+ mTetheringEventCallback.unregister();
+ mTetheringEventCallback = null;
+ }
+ });
if (mDownstreamReader != null) {
TapPacketReader reader = mDownstreamReader;
mHandler.post(() -> reader.stop());
mDownstreamReader = null;
}
- mTetheredInterfaceRequester.release();
- mEm.setIncludeTestInterfaces(false);
+ runAsShell(NETWORK_SETTINGS, () -> {
+ mTetheredInterfaceRequester.release();
+ });
+ setIncludeTestInterfaces(false);
maybeDeleteTestInterface();
}
@@ -262,10 +263,24 @@
} catch (TimeoutException e) {
return false;
} finally {
- requester.release();
+ runAsShell(NETWORK_SETTINGS, () -> {
+ requester.release();
+ });
}
}
+ private void setIncludeTestInterfaces(boolean include) {
+ runAsShell(NETWORK_SETTINGS, () -> {
+ mEm.setIncludeTestInterfaces(include);
+ });
+ }
+
+ private void setPreferTestNetworks(boolean prefer) {
+ runAsShell(NETWORK_SETTINGS, () -> {
+ mTm.setPreferTestNetworks(prefer);
+ });
+ }
+
@Test
public void testVirtualEthernetAlreadyExists() throws Exception {
// This test requires manipulating packets. Skip if there is a physical Ethernet connected.
@@ -279,7 +294,7 @@
int mtu = getMTU(mDownstreamIface);
Log.d(TAG, "Including test interfaces");
- mEm.setIncludeTestInterfaces(true);
+ setIncludeTestInterfaces(true);
final String iface = mTetheredInterfaceRequester.getInterface();
assertEquals("TetheredInterfaceCallback for unexpected interface",
@@ -295,7 +310,7 @@
CompletableFuture<String> futureIface = mTetheredInterfaceRequester.requestInterface();
- mEm.setIncludeTestInterfaces(true);
+ setIncludeTestInterfaces(true);
mDownstreamIface = createTestInterface();
@@ -310,7 +325,7 @@
public void testStaticIpv4() throws Exception {
assumeFalse(isInterfaceForTetheringAvailable());
- mEm.setIncludeTestInterfaces(true);
+ setIncludeTestInterfaces(true);
mDownstreamIface = createTestInterface();
@@ -388,7 +403,7 @@
public void testLocalOnlyTethering() throws Exception {
assumeFalse(isInterfaceForTetheringAvailable());
- mEm.setIncludeTestInterfaces(true);
+ setIncludeTestInterfaces(true);
mDownstreamIface = createTestInterface();
@@ -461,6 +476,7 @@
private final CountDownLatch mLocalOnlyStoppedLatch = new CountDownLatch(1);
private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1);
private final CountDownLatch mUpstreamLatch = new CountDownLatch(1);
+ private final CountDownLatch mCallbackRegisteredLatch = new CountDownLatch(1);
private final TetheringInterface mIface;
private final Network mExpectedUpstream;
@@ -539,6 +555,22 @@
mLocalOnlyStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
+ // Used to check if the callback has registered. When the callback is registered,
+ // onSupportedTetheringTypes is celled in onCallbackStarted(). After
+ // onSupportedTetheringTypes called, drop the permission for registering callback.
+ // See MyTetheringEventCallback#register, TetheringManager#onCallbackStarted.
+ @Override
+ public void onSupportedTetheringTypes(Set<Integer> supportedTypes) {
+ // Used to check callback registered.
+ mCallbackRegisteredLatch.countDown();
+ }
+
+ public void awaitCallbackRegistered() throws Exception {
+ if (!mCallbackRegisteredLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Did not receive callback registered signal after " + TIMEOUT_MS + "ms");
+ }
+ }
+
public void awaitInterfaceUntethered() throws Exception {
// Don't block teardown if the interface was never tethered.
// This is racy because the interface might become tethered right after this check, but
@@ -614,16 +646,34 @@
} else {
callback = new MyTetheringEventCallback(mTm, iface);
}
- mTm.registerTetheringEventCallback(mHandler::post, callback);
-
+ runAsShell(NETWORK_SETTINGS, () -> {
+ mTm.registerTetheringEventCallback(mHandler::post, callback);
+ // Need to hold the shell permission until callback is registered. This helps to avoid
+ // the test become flaky.
+ callback.awaitCallbackRegistered();
+ });
+ final CountDownLatch tetheringStartedLatch = new CountDownLatch(1);
StartTetheringCallback startTetheringCallback = new StartTetheringCallback() {
@Override
+ public void onTetheringStarted() {
+ Log.d(TAG, "Ethernet tethering started");
+ tetheringStartedLatch.countDown();
+ }
+
+ @Override
public void onTetheringFailed(int resultCode) {
fail("Unexpectedly got onTetheringFailed");
}
};
Log.d(TAG, "Starting Ethernet tethering");
- mTm.startTethering(request, mHandler::post /* executor */, startTetheringCallback);
+ runAsShell(TETHER_PRIVILEGED, () -> {
+ mTm.startTethering(request, mHandler::post /* executor */, startTetheringCallback);
+ // Binder call is an async call. Need to hold the shell permission until tethering
+ // started. This helps to avoid the test become flaky.
+ if (!tetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("Did not receive tethering started callback after " + TIMEOUT_MS + "ms");
+ }
+ });
final int connectivityType = request.getConnectivityScope();
switch (connectivityType) {
@@ -731,7 +781,8 @@
public CompletableFuture<String> requestInterface() {
assertNull("BUG: more than one tethered interface request", mRequest);
Log.d(TAG, "Requesting tethered interface");
- mRequest = mEm.requestTetheredInterface(mHandler::post, this);
+ mRequest = runAsShell(NETWORK_SETTINGS, () ->
+ mEm.requestTetheredInterface(mHandler::post, this));
return mFuture;
}
@@ -791,8 +842,10 @@
}
private TestNetworkInterface createTestInterface() throws Exception {
- TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class);
- TestNetworkInterface iface = tnm.createTapInterface();
+ TestNetworkManager tnm = runAsShell(MANAGE_TEST_NETWORKS, () ->
+ mContext.getSystemService(TestNetworkManager.class));
+ TestNetworkInterface iface = runAsShell(MANAGE_TEST_NETWORKS, () ->
+ tnm.createTapInterface());
Log.d(TAG, "Created test interface " + iface.getInterfaceName());
return iface;
}
@@ -807,14 +860,14 @@
private TestNetworkTracker createTestUpstream(final List<LinkAddress> addresses,
final List<InetAddress> dnses) throws Exception {
- mTm.setPreferTestNetworks(true);
+ setPreferTestNetworks(true);
final LinkProperties lp = new LinkProperties();
lp.setLinkAddresses(addresses);
lp.setDnsServers(dnses);
lp.setNat64Prefix(TEST_NAT64PREFIX);
- return initTestNetwork(mContext, lp, TIMEOUT_MS);
+ return runAsShell(MANAGE_TEST_NETWORKS, () -> initTestNetwork(mContext, lp, TIMEOUT_MS));
}
@Test
@@ -1126,7 +1179,7 @@
mUpstreamTracker = createTestUpstream(upstreamAddresses, upstreamDnses);
mDownstreamIface = createTestInterface();
- mEm.setIncludeTestInterfaces(true);
+ setIncludeTestInterfaces(true);
// Make sure EtherentTracker use "mDownstreamIface" as server mode interface.
assertEquals("TetheredInterfaceCallback for unexpected interface",
@@ -1251,7 +1304,8 @@
Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
throws Exception {
final String[] args = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, mapArg};
- final String rawMapStr = DumpTestUtils.dumpService(Context.TETHERING_SERVICE, args);
+ final String rawMapStr = runAsShell(DUMP, () ->
+ DumpTestUtils.dumpService(Context.TETHERING_SERVICE, args));
final HashMap<K, V> map = new HashMap<>();
for (final String line : rawMapStr.split(LINE_DELIMITER)) {
@@ -1277,7 +1331,8 @@
}
private boolean isTetherConfigBpfOffloadEnabled() throws Exception {
- final String dumpStr = DumpTestUtils.dumpService(Context.TETHERING_SERVICE, "--short");
+ final String dumpStr = runAsShell(DUMP, () ->
+ DumpTestUtils.dumpService(Context.TETHERING_SERVICE, "--short"));
// BPF offload tether config can be overridden by "config_tether_enable_bpf_offload" in
// packages/modules/Connectivity/Tethering/res/values/config.xml. OEM may disable config by