[Thread] unify synchronous controller API for testing
This commit creates the new `ThreadNetworkControllerWrapper` class to unify the asynchronous version of the ThreadNetworkController API to simplify Thread tests. Test: atest ThreadNetworkIntegrationTests Change-Id: Ibc6e6e2dad5041b092315aa0278915c137867488
This commit is contained in:
parent
c1df0d07dd
commit
62eac0132d
4 changed files with 231 additions and 165 deletions
|
|
@ -17,10 +17,7 @@
|
|||
package android.net.thread;
|
||||
|
||||
import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
|
||||
import static android.Manifest.permission.NETWORK_SETTINGS;
|
||||
import static android.net.thread.ThreadNetworkManager.PERMISSION_THREAD_NETWORK_PRIVILEGED;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.JOIN_TIMEOUT;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.RESTART_JOIN_TIMEOUT;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.isExpectedIcmpv6Packet;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.isFromIpv6Source;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.isInMulticastGroup;
|
||||
|
|
@ -36,13 +33,14 @@ import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
|
|||
import static com.android.testutils.TestPermissionUtil.runAsShell;
|
||||
|
||||
import static com.google.common.io.BaseEncoding.base16;
|
||||
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.InetAddresses;
|
||||
import android.net.LinkProperties;
|
||||
|
|
@ -54,6 +52,7 @@ import android.net.thread.utils.ThreadFeatureCheckerRule;
|
|||
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresIpv6MulticastRouting;
|
||||
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresSimulationThreadDevice;
|
||||
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
|
||||
import android.net.thread.utils.ThreadNetworkControllerWrapper;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
|
||||
|
|
@ -74,9 +73,6 @@ import java.net.Inet6Address;
|
|||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/** Integration test cases for Thread Border Routing feature. */
|
||||
|
|
@ -108,7 +104,8 @@ public class BorderRoutingTest {
|
|||
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
|
||||
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private ThreadNetworkController mController;
|
||||
private final ThreadNetworkControllerWrapper mController =
|
||||
ThreadNetworkControllerWrapper.newInstance(mContext);
|
||||
private OtDaemonController mOtCtl;
|
||||
private HandlerThread mHandlerThread;
|
||||
private Handler mHandler;
|
||||
|
|
@ -119,11 +116,6 @@ public class BorderRoutingTest {
|
|||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
final ThreadNetworkManager manager = mContext.getSystemService(ThreadNetworkManager.class);
|
||||
if (manager != null) {
|
||||
mController = manager.getAllThreadNetworkControllers().get(0);
|
||||
}
|
||||
|
||||
// TODO: b/323301831 - This is a workaround to avoid unnecessary delay to re-form a network
|
||||
mOtCtl = new OtDaemonController();
|
||||
mOtCtl.factoryReset();
|
||||
|
|
@ -134,9 +126,7 @@ public class BorderRoutingTest {
|
|||
mFtds = new ArrayList<>();
|
||||
|
||||
setUpInfraNetwork();
|
||||
|
||||
// BR forms a network.
|
||||
startBrLeader();
|
||||
mController.joinAndWait(DEFAULT_DATASET);
|
||||
|
||||
// Creates a infra network device.
|
||||
mInfraNetworkReader = newPacketReader(mInfraNetworkTracker.getTestIface(), mHandler);
|
||||
|
|
@ -150,16 +140,8 @@ public class BorderRoutingTest {
|
|||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
runAsShell(
|
||||
PERMISSION_THREAD_NETWORK_PRIVILEGED,
|
||||
NETWORK_SETTINGS,
|
||||
() -> {
|
||||
CountDownLatch latch = new CountDownLatch(2);
|
||||
mController.setTestNetworkAsUpstream(
|
||||
null, directExecutor(), v -> latch.countDown());
|
||||
mController.leave(directExecutor(), v -> latch.countDown());
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
});
|
||||
mController.setTestNetworkAsUpstreamAndWait(null);
|
||||
mController.leaveAndWait();
|
||||
tearDownInfraNetwork();
|
||||
|
||||
mHandlerThread.quitSafely();
|
||||
|
|
@ -205,9 +187,6 @@ public class BorderRoutingTest {
|
|||
* </pre>
|
||||
*/
|
||||
|
||||
// Form the network.
|
||||
mOtCtl.factoryReset();
|
||||
startBrLeader();
|
||||
startInfraDevice();
|
||||
FullThreadDevice ftd = mFtds.get(0);
|
||||
startFtdChild(ftd);
|
||||
|
|
@ -229,12 +208,10 @@ public class BorderRoutingTest {
|
|||
* </pre>
|
||||
*/
|
||||
|
||||
// Creates a Full Thread Device (FTD) and lets it join the network.
|
||||
FullThreadDevice ftd = mFtds.get(0);
|
||||
startFtdChild(ftd);
|
||||
Inet6Address ftdOmr = ftd.getOmrAddress();
|
||||
Inet6Address ftdMlEid = ftd.getMlEid();
|
||||
assertNotNull(ftdMlEid);
|
||||
Inet6Address ftdOmr = requireNonNull(ftd.getOmrAddress());
|
||||
Inet6Address ftdMlEid = requireNonNull(ftd.getMlEid());
|
||||
|
||||
ftd.udpBind(ftdOmr, 12345);
|
||||
sendUdpMessage(ftdOmr, 12345, "aaaaaaaa");
|
||||
|
|
@ -588,38 +565,21 @@ public class BorderRoutingTest {
|
|||
pollForPacketOnInfraNetwork(ICMPV6_ECHO_REQUEST_TYPE, ftdOmr, GROUP_ADDR_SCOPE_4));
|
||||
}
|
||||
|
||||
private void setUpInfraNetwork() {
|
||||
private void setUpInfraNetwork() throws Exception {
|
||||
mInfraNetworkTracker =
|
||||
runAsShell(
|
||||
MANAGE_TEST_NETWORKS,
|
||||
() ->
|
||||
initTestNetwork(
|
||||
mContext, new LinkProperties(), 5000 /* timeoutMs */));
|
||||
runAsShell(
|
||||
PERMISSION_THREAD_NETWORK_PRIVILEGED,
|
||||
NETWORK_SETTINGS,
|
||||
() -> {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
mController.setTestNetworkAsUpstream(
|
||||
mInfraNetworkTracker.getTestIface().getInterfaceName(),
|
||||
directExecutor(),
|
||||
future::complete);
|
||||
future.get(5, TimeUnit.SECONDS);
|
||||
});
|
||||
mController.setTestNetworkAsUpstreamAndWait(
|
||||
mInfraNetworkTracker.getTestIface().getInterfaceName());
|
||||
}
|
||||
|
||||
private void tearDownInfraNetwork() {
|
||||
runAsShell(MANAGE_TEST_NETWORKS, () -> mInfraNetworkTracker.teardown());
|
||||
}
|
||||
|
||||
private void startBrLeader() throws Exception {
|
||||
CompletableFuture<Void> joinFuture = new CompletableFuture<>();
|
||||
runAsShell(
|
||||
PERMISSION_THREAD_NETWORK_PRIVILEGED,
|
||||
() -> mController.join(DEFAULT_DATASET, directExecutor(), joinFuture::complete));
|
||||
joinFuture.get(RESTART_JOIN_TIMEOUT.toSeconds(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void startFtdChild(FullThreadDevice ftd) throws Exception {
|
||||
ftd.factoryReset();
|
||||
ftd.joinNetwork(DEFAULT_DATASET);
|
||||
|
|
|
|||
|
|
@ -16,11 +16,8 @@
|
|||
|
||||
package android.net.thread;
|
||||
|
||||
import static android.Manifest.permission.NETWORK_SETTINGS;
|
||||
import static android.net.InetAddresses.parseNumericAddress;
|
||||
import static android.net.thread.ThreadNetworkManager.PERMISSION_THREAD_NETWORK_PRIVILEGED;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.JOIN_TIMEOUT;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.RESTART_JOIN_TIMEOUT;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.SERVICE_DISCOVERY_TIMEOUT;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.discoverForServiceLost;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.discoverService;
|
||||
|
|
@ -28,16 +25,12 @@ import static android.net.thread.utils.IntegrationTestUtils.resolveService;
|
|||
import static android.net.thread.utils.IntegrationTestUtils.resolveServiceUntil;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.waitFor;
|
||||
|
||||
import static com.android.testutils.TestPermissionUtil.runAsShell;
|
||||
|
||||
import static com.google.common.io.BaseEncoding.base16;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
||||
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.nsd.NsdManager;
|
||||
|
|
@ -47,6 +40,7 @@ import android.net.thread.utils.TapTestNetworkTracker;
|
|||
import android.net.thread.utils.ThreadFeatureCheckerRule;
|
||||
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresSimulationThreadDevice;
|
||||
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
|
||||
import android.net.thread.utils.ThreadNetworkControllerWrapper;
|
||||
import android.os.HandlerThread;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
|
@ -99,27 +93,18 @@ public class ServiceDiscoveryTest {
|
|||
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
|
||||
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private final ThreadNetworkControllerWrapper mController =
|
||||
ThreadNetworkControllerWrapper.newInstance(mContext);
|
||||
|
||||
private HandlerThread mHandlerThread;
|
||||
private ThreadNetworkController mController;
|
||||
private NsdManager mNsdManager;
|
||||
private TapTestNetworkTracker mTestNetworkTracker;
|
||||
private List<FullThreadDevice> mFtds;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
final ThreadNetworkManager manager = mContext.getSystemService(ThreadNetworkManager.class);
|
||||
if (manager != null) {
|
||||
mController = manager.getAllThreadNetworkControllers().get(0);
|
||||
}
|
||||
|
||||
// BR forms a network.
|
||||
CompletableFuture<Void> joinFuture = new CompletableFuture<>();
|
||||
runAsShell(
|
||||
PERMISSION_THREAD_NETWORK_PRIVILEGED,
|
||||
() -> mController.join(DEFAULT_DATASET, directExecutor(), joinFuture::complete));
|
||||
joinFuture.get(RESTART_JOIN_TIMEOUT.toMillis(), MILLISECONDS);
|
||||
|
||||
mController.joinAndWait(DEFAULT_DATASET);
|
||||
mNsdManager = mContext.getSystemService(NsdManager.class);
|
||||
|
||||
mHandlerThread = new HandlerThread(TAG);
|
||||
|
|
@ -127,17 +112,8 @@ public class ServiceDiscoveryTest {
|
|||
|
||||
mTestNetworkTracker = new TapTestNetworkTracker(mContext, mHandlerThread.getLooper());
|
||||
assertThat(mTestNetworkTracker).isNotNull();
|
||||
runAsShell(
|
||||
PERMISSION_THREAD_NETWORK_PRIVILEGED,
|
||||
NETWORK_SETTINGS,
|
||||
() -> {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
mController.setTestNetworkAsUpstream(
|
||||
mTestNetworkTracker.getInterfaceName(),
|
||||
directExecutor(),
|
||||
v -> future.complete(null));
|
||||
future.get(5, SECONDS);
|
||||
});
|
||||
mController.setTestNetworkAsUpstreamAndWait(mTestNetworkTracker.getInterfaceName());
|
||||
|
||||
// Create the FTDs in setUp() so that the FTDs can be safely released in tearDown().
|
||||
// Don't create new FTDs in test cases.
|
||||
mFtds = new ArrayList<>();
|
||||
|
|
@ -164,18 +140,8 @@ public class ServiceDiscoveryTest {
|
|||
mHandlerThread.quitSafely();
|
||||
mHandlerThread.join();
|
||||
}
|
||||
runAsShell(
|
||||
PERMISSION_THREAD_NETWORK_PRIVILEGED,
|
||||
NETWORK_SETTINGS,
|
||||
() -> {
|
||||
CompletableFuture<Void> setUpstreamFuture = new CompletableFuture<>();
|
||||
CompletableFuture<Void> leaveFuture = new CompletableFuture<>();
|
||||
mController.setTestNetworkAsUpstream(
|
||||
null, directExecutor(), v -> setUpstreamFuture.complete(null));
|
||||
mController.leave(directExecutor(), v -> leaveFuture.complete(null));
|
||||
setUpstreamFuture.get(5, SECONDS);
|
||||
leaveFuture.get(5, SECONDS);
|
||||
});
|
||||
mController.setTestNetworkAsUpstreamAndWait(null);
|
||||
mController.leaveAndWait();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -16,31 +16,22 @@
|
|||
|
||||
package android.net.thread;
|
||||
|
||||
import static android.Manifest.permission.NETWORK_SETTINGS;
|
||||
import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_DETACHED;
|
||||
import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_LEADER;
|
||||
import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_STOPPED;
|
||||
import static android.net.thread.ThreadNetworkManager.PERMISSION_THREAD_NETWORK_PRIVILEGED;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.CALLBACK_TIMEOUT;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.LEAVE_TIMEOUT;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.RESTART_JOIN_TIMEOUT;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.waitForStateAnyOf;
|
||||
|
||||
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
|
||||
import static com.android.testutils.TestPermissionUtil.runAsShell;
|
||||
|
||||
import static com.google.common.io.BaseEncoding.base16;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.net.thread.ThreadNetworkController.StateCallback;
|
||||
import android.net.thread.utils.OtDaemonController;
|
||||
import android.net.thread.utils.ThreadFeatureCheckerRule;
|
||||
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
|
||||
import android.net.thread.utils.ThreadNetworkControllerWrapper;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
|
@ -55,7 +46,6 @@ import org.junit.runner.RunWith;
|
|||
|
||||
import java.net.Inet6Address;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/** Tests for E2E Android Thread integration with ot-daemon, ConnectivityService, etc.. */
|
||||
@LargeTest
|
||||
|
|
@ -76,17 +66,14 @@ public class ThreadIntegrationTest {
|
|||
@Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
|
||||
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private ThreadNetworkController mController;
|
||||
private final ThreadNetworkControllerWrapper mController =
|
||||
ThreadNetworkControllerWrapper.newInstance(mContext);
|
||||
private OtDaemonController mOtCtl;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mController =
|
||||
mContext.getSystemService(ThreadNetworkManager.class)
|
||||
.getAllThreadNetworkControllers()
|
||||
.get(0);
|
||||
mOtCtl = new OtDaemonController();
|
||||
leaveAndWait(mController);
|
||||
mController.leaveAndWait();
|
||||
|
||||
// TODO: b/323301831 - This is a workaround to avoid unnecessary delay to re-form a network
|
||||
mOtCtl.factoryReset();
|
||||
|
|
@ -94,43 +81,43 @@ public class ThreadIntegrationTest {
|
|||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
setTestUpStreamNetworkAndWait(mController, null);
|
||||
leaveAndWait(mController);
|
||||
mController.setTestNetworkAsUpstreamAndWait(null);
|
||||
mController.leaveAndWait();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void otDaemonRestart_notJoinedAndStopped_deviceRoleIsStopped() throws Exception {
|
||||
leaveAndWait(mController);
|
||||
mController.leaveAndWait();
|
||||
|
||||
runShellCommand("stop ot-daemon");
|
||||
// TODO(b/323331973): the sleep is needed to workaround the race conditions
|
||||
SystemClock.sleep(200);
|
||||
|
||||
waitForStateAnyOf(mController, List.of(DEVICE_ROLE_STOPPED), CALLBACK_TIMEOUT);
|
||||
mController.waitForRole(DEVICE_ROLE_STOPPED, CALLBACK_TIMEOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void otDaemonRestart_JoinedNetworkAndStopped_autoRejoined() throws Exception {
|
||||
joinAndWait(mController, DEFAULT_DATASET);
|
||||
mController.joinAndWait(DEFAULT_DATASET);
|
||||
|
||||
runShellCommand("stop ot-daemon");
|
||||
|
||||
waitForStateAnyOf(mController, List.of(DEVICE_ROLE_DETACHED), CALLBACK_TIMEOUT);
|
||||
waitForStateAnyOf(mController, List.of(DEVICE_ROLE_LEADER), RESTART_JOIN_TIMEOUT);
|
||||
mController.waitForRole(DEVICE_ROLE_DETACHED, CALLBACK_TIMEOUT);
|
||||
mController.waitForRole(DEVICE_ROLE_LEADER, RESTART_JOIN_TIMEOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void otDaemonFactoryReset_deviceRoleIsStopped() throws Exception {
|
||||
joinAndWait(mController, DEFAULT_DATASET);
|
||||
mController.joinAndWait(DEFAULT_DATASET);
|
||||
|
||||
mOtCtl.factoryReset();
|
||||
|
||||
assertThat(getDeviceRole(mController)).isEqualTo(DEVICE_ROLE_STOPPED);
|
||||
assertThat(mController.getDeviceRole()).isEqualTo(DEVICE_ROLE_STOPPED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void otDaemonFactoryReset_addressesRemoved() throws Exception {
|
||||
joinAndWait(mController, DEFAULT_DATASET);
|
||||
mController.joinAndWait(DEFAULT_DATASET);
|
||||
|
||||
mOtCtl.factoryReset();
|
||||
String ifconfig = runShellCommand("ifconfig thread-wpan");
|
||||
|
|
@ -140,7 +127,7 @@ public class ThreadIntegrationTest {
|
|||
|
||||
@Test
|
||||
public void tunInterface_joinedNetwork_otAddressesAddedToTunInterface() throws Exception {
|
||||
joinAndWait(mController, DEFAULT_DATASET);
|
||||
mController.joinAndWait(DEFAULT_DATASET);
|
||||
|
||||
String ifconfig = runShellCommand("ifconfig thread-wpan");
|
||||
List<Inet6Address> otAddresses = mOtCtl.getAddresses();
|
||||
|
|
@ -152,46 +139,4 @@ public class ThreadIntegrationTest {
|
|||
|
||||
// TODO (b/323300829): add more tests for integration with linux platform and
|
||||
// ConnectivityService
|
||||
|
||||
private static int getDeviceRole(ThreadNetworkController controller) throws Exception {
|
||||
CompletableFuture<Integer> future = new CompletableFuture<>();
|
||||
StateCallback callback = future::complete;
|
||||
controller.registerStateCallback(directExecutor(), callback);
|
||||
try {
|
||||
return future.get(CALLBACK_TIMEOUT.toMillis(), MILLISECONDS);
|
||||
} finally {
|
||||
controller.unregisterStateCallback(callback);
|
||||
}
|
||||
}
|
||||
|
||||
private static void joinAndWait(
|
||||
ThreadNetworkController controller, ActiveOperationalDataset activeDataset)
|
||||
throws Exception {
|
||||
runAsShell(
|
||||
PERMISSION_THREAD_NETWORK_PRIVILEGED,
|
||||
() -> controller.join(activeDataset, directExecutor(), result -> {}));
|
||||
waitForStateAnyOf(controller, List.of(DEVICE_ROLE_LEADER), RESTART_JOIN_TIMEOUT);
|
||||
}
|
||||
|
||||
private static void leaveAndWait(ThreadNetworkController controller) throws Exception {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
runAsShell(
|
||||
PERMISSION_THREAD_NETWORK_PRIVILEGED,
|
||||
() -> controller.leave(directExecutor(), future::complete));
|
||||
future.get(LEAVE_TIMEOUT.toMillis(), MILLISECONDS);
|
||||
}
|
||||
|
||||
private static void setTestUpStreamNetworkAndWait(
|
||||
ThreadNetworkController controller, @Nullable String networkInterfaceName)
|
||||
throws Exception {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
runAsShell(
|
||||
PERMISSION_THREAD_NETWORK_PRIVILEGED,
|
||||
NETWORK_SETTINGS,
|
||||
() -> {
|
||||
controller.setTestNetworkAsUpstream(
|
||||
networkInterfaceName, directExecutor(), future::complete);
|
||||
});
|
||||
future.get(CALLBACK_TIMEOUT.toMillis(), MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* 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 android.net.thread.utils;
|
||||
|
||||
import static android.Manifest.permission.ACCESS_NETWORK_STATE;
|
||||
import static android.Manifest.permission.NETWORK_SETTINGS;
|
||||
import static android.net.thread.ThreadNetworkManager.PERMISSION_THREAD_NETWORK_PRIVILEGED;
|
||||
import static android.net.thread.utils.IntegrationTestUtils.CALLBACK_TIMEOUT;
|
||||
|
||||
import static com.android.testutils.TestPermissionUtil.runAsShell;
|
||||
|
||||
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.net.thread.ActiveOperationalDataset;
|
||||
import android.net.thread.ThreadNetworkController;
|
||||
import android.net.thread.ThreadNetworkController.StateCallback;
|
||||
import android.net.thread.ThreadNetworkException;
|
||||
import android.net.thread.ThreadNetworkManager;
|
||||
import android.os.OutcomeReceiver;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/** A helper class which provides synchronous API wrappers for {@link ThreadNetworkController}. */
|
||||
public final class ThreadNetworkControllerWrapper {
|
||||
public static final Duration JOIN_TIMEOUT = Duration.ofSeconds(10);
|
||||
public static final Duration LEAVE_TIMEOUT = Duration.ofSeconds(2);
|
||||
private static final Duration CALLBACK_TIMEOUT = Duration.ofSeconds(1);
|
||||
|
||||
private final ThreadNetworkController mController;
|
||||
|
||||
/**
|
||||
* Returns a new {@link ThreadNetworkControllerWrapper} instance or {@code null} if Thread
|
||||
* feature is not supported on this device.
|
||||
*/
|
||||
@Nullable
|
||||
public static ThreadNetworkControllerWrapper newInstance(Context context) {
|
||||
final ThreadNetworkManager manager = context.getSystemService(ThreadNetworkManager.class);
|
||||
if (manager == null) {
|
||||
return null;
|
||||
}
|
||||
return new ThreadNetworkControllerWrapper(manager.getAllThreadNetworkControllers().get(0));
|
||||
}
|
||||
|
||||
private ThreadNetworkControllerWrapper(ThreadNetworkController controller) {
|
||||
mController = controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Thread enabled state.
|
||||
*
|
||||
* <p>The value can be one of {@code ThreadNetworkController#STATE_*}.
|
||||
*/
|
||||
public final int getEnabledState()
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
CompletableFuture<Integer> future = new CompletableFuture<>();
|
||||
StateCallback callback =
|
||||
new StateCallback() {
|
||||
@Override
|
||||
public void onThreadEnableStateChanged(int enabledState) {
|
||||
future.complete(enabledState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceRoleChanged(int deviceRole) {}
|
||||
};
|
||||
|
||||
runAsShell(
|
||||
ACCESS_NETWORK_STATE,
|
||||
() -> mController.registerStateCallback(directExecutor(), callback));
|
||||
try {
|
||||
return future.get(CALLBACK_TIMEOUT.toSeconds(), SECONDS);
|
||||
} finally {
|
||||
runAsShell(ACCESS_NETWORK_STATE, () -> mController.unregisterStateCallback(callback));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Thread device role.
|
||||
*
|
||||
* <p>The value can be one of {@code ThreadNetworkController#DEVICE_ROLE_*}.
|
||||
*/
|
||||
public final int getDeviceRole()
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
CompletableFuture<Integer> future = new CompletableFuture<>();
|
||||
StateCallback callback = future::complete;
|
||||
|
||||
runAsShell(
|
||||
ACCESS_NETWORK_STATE,
|
||||
() -> mController.registerStateCallback(directExecutor(), callback));
|
||||
try {
|
||||
return future.get(CALLBACK_TIMEOUT.toSeconds(), SECONDS);
|
||||
} finally {
|
||||
runAsShell(ACCESS_NETWORK_STATE, () -> mController.unregisterStateCallback(callback));
|
||||
}
|
||||
}
|
||||
|
||||
/** Joins the given network and wait for this device to become attached. */
|
||||
public void joinAndWait(ActiveOperationalDataset activeDataset)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
runAsShell(
|
||||
PERMISSION_THREAD_NETWORK_PRIVILEGED,
|
||||
() ->
|
||||
mController.join(
|
||||
activeDataset, directExecutor(), newOutcomeReceiver(future)));
|
||||
future.get(JOIN_TIMEOUT.toSeconds(), SECONDS);
|
||||
}
|
||||
|
||||
/** An synchronous variant of {@link ThreadNetworkController#leave}. */
|
||||
public void leaveAndWait() throws InterruptedException, ExecutionException, TimeoutException {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
runAsShell(
|
||||
PERMISSION_THREAD_NETWORK_PRIVILEGED,
|
||||
() -> mController.leave(directExecutor(), future::complete));
|
||||
future.get(LEAVE_TIMEOUT.toSeconds(), SECONDS);
|
||||
}
|
||||
|
||||
/** Waits for the device role to become {@code deviceRole}. */
|
||||
public int waitForRole(int deviceRole, Duration timeout)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return waitForRoleAnyOf(List.of(deviceRole), timeout);
|
||||
}
|
||||
|
||||
/** Waits for the device role to become one of the values specified in {@code deviceRoles}. */
|
||||
public int waitForRoleAnyOf(List<Integer> deviceRoles, Duration timeout)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
CompletableFuture<Integer> future = new CompletableFuture<>();
|
||||
ThreadNetworkController.StateCallback callback =
|
||||
newRole -> {
|
||||
if (deviceRoles.contains(newRole)) {
|
||||
future.complete(newRole);
|
||||
}
|
||||
};
|
||||
|
||||
runAsShell(
|
||||
ACCESS_NETWORK_STATE,
|
||||
() -> mController.registerStateCallback(directExecutor(), callback));
|
||||
|
||||
try {
|
||||
return future.get(timeout.toSeconds(), SECONDS);
|
||||
} finally {
|
||||
mController.unregisterStateCallback(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/** An synchronous variant of {@link ThreadNetworkController#setTestNetworkAsUpstream}. */
|
||||
public void setTestNetworkAsUpstreamAndWait(@Nullable String networkInterfaceName)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
runAsShell(
|
||||
PERMISSION_THREAD_NETWORK_PRIVILEGED,
|
||||
NETWORK_SETTINGS,
|
||||
() -> {
|
||||
mController.setTestNetworkAsUpstream(
|
||||
networkInterfaceName, directExecutor(), future::complete);
|
||||
});
|
||||
future.get(CALLBACK_TIMEOUT.toSeconds(), SECONDS);
|
||||
}
|
||||
|
||||
private static <V> OutcomeReceiver<V, ThreadNetworkException> newOutcomeReceiver(
|
||||
CompletableFuture<V> future) {
|
||||
return new OutcomeReceiver<V, ThreadNetworkException>() {
|
||||
@Override
|
||||
public void onResult(V result) {
|
||||
future.complete(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ThreadNetworkException e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue