1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.net.thread.utils; 17 18 import static android.Manifest.permission.ACCESS_NETWORK_STATE; 19 import static android.Manifest.permission.NETWORK_SETTINGS; 20 import static android.net.thread.ThreadNetworkManager.PERMISSION_THREAD_NETWORK_PRIVILEGED; 21 import static android.net.thread.utils.IntegrationTestUtils.CALLBACK_TIMEOUT; 22 23 import static com.android.testutils.TestPermissionUtil.runAsShell; 24 25 import static com.google.common.util.concurrent.MoreExecutors.directExecutor; 26 27 import static java.util.concurrent.TimeUnit.SECONDS; 28 29 import android.annotation.Nullable; 30 import android.content.Context; 31 import android.net.thread.ActiveOperationalDataset; 32 import android.net.thread.ThreadConfiguration; 33 import android.net.thread.ThreadNetworkController; 34 import android.net.thread.ThreadNetworkController.StateCallback; 35 import android.net.thread.ThreadNetworkException; 36 import android.net.thread.ThreadNetworkManager; 37 import android.os.OutcomeReceiver; 38 39 import java.time.Duration; 40 import java.util.ArrayList; 41 import java.util.List; 42 import java.util.concurrent.CompletableFuture; 43 import java.util.concurrent.ExecutionException; 44 import java.util.concurrent.TimeoutException; 45 import java.util.function.Consumer; 46 47 /** A helper class which provides synchronous API wrappers for {@link ThreadNetworkController}. */ 48 public final class ThreadNetworkControllerWrapper { 49 public static final Duration JOIN_TIMEOUT = Duration.ofSeconds(10); 50 public static final Duration LEAVE_TIMEOUT = Duration.ofSeconds(2); 51 private static final Duration CALLBACK_TIMEOUT = Duration.ofSeconds(1); 52 private static final Duration SET_ENABLED_TIMEOUT = Duration.ofSeconds(2); 53 private static final Duration CONFIG_TIMEOUT = Duration.ofSeconds(1); 54 55 private final ThreadNetworkController mController; 56 57 private final List<Integer> mDeviceRoleUpdates = new ArrayList<>(); 58 @Nullable private StateCallback mStateCallback; 59 60 /** 61 * Returns a new {@link ThreadNetworkControllerWrapper} instance or {@code null} if Thread 62 * feature is not supported on this device. 63 */ 64 @Nullable newInstance(Context context)65 public static ThreadNetworkControllerWrapper newInstance(Context context) { 66 final ThreadNetworkManager manager = context.getSystemService(ThreadNetworkManager.class); 67 if (manager == null) { 68 return null; 69 } 70 return new ThreadNetworkControllerWrapper(manager.getAllThreadNetworkControllers().get(0)); 71 } 72 ThreadNetworkControllerWrapper(ThreadNetworkController controller)73 private ThreadNetworkControllerWrapper(ThreadNetworkController controller) { 74 mController = controller; 75 } 76 77 /** 78 * Returns the underlying {@link ThreadNetworkController} object or {@code null} if the current 79 * platform doesn't support it. 80 */ 81 @Nullable get()82 public ThreadNetworkController get() { 83 return mController; 84 } 85 86 /** 87 * Returns the Thread enabled state. 88 * 89 * <p>The value can be one of {@code ThreadNetworkController#STATE_*}. 90 */ getEnabledState()91 public final int getEnabledState() 92 throws InterruptedException, ExecutionException, TimeoutException { 93 CompletableFuture<Integer> future = new CompletableFuture<>(); 94 StateCallback callback = 95 new StateCallback() { 96 @Override 97 public void onThreadEnableStateChanged(int enabledState) { 98 future.complete(enabledState); 99 } 100 101 @Override 102 public void onDeviceRoleChanged(int deviceRole) {} 103 }; 104 105 runAsShell( 106 ACCESS_NETWORK_STATE, 107 () -> mController.registerStateCallback(directExecutor(), callback)); 108 try { 109 return future.get(CALLBACK_TIMEOUT.toSeconds(), SECONDS); 110 } finally { 111 runAsShell(ACCESS_NETWORK_STATE, () -> mController.unregisterStateCallback(callback)); 112 } 113 } 114 115 /** 116 * Returns the Thread device role. 117 * 118 * <p>The value can be one of {@code ThreadNetworkController#DEVICE_ROLE_*}. 119 */ getDeviceRole()120 public final int getDeviceRole() 121 throws InterruptedException, ExecutionException, TimeoutException { 122 CompletableFuture<Integer> future = new CompletableFuture<>(); 123 StateCallback callback = future::complete; 124 125 runAsShell( 126 ACCESS_NETWORK_STATE, 127 () -> mController.registerStateCallback(directExecutor(), callback)); 128 try { 129 return future.get(CALLBACK_TIMEOUT.toSeconds(), SECONDS); 130 } finally { 131 runAsShell(ACCESS_NETWORK_STATE, () -> mController.unregisterStateCallback(callback)); 132 } 133 } 134 135 /** An synchronous variant of {@link ThreadNetworkController#setEnabled}. */ setEnabledAndWait(boolean enabled)136 public void setEnabledAndWait(boolean enabled) 137 throws InterruptedException, ExecutionException, TimeoutException { 138 CompletableFuture<Void> future = new CompletableFuture<>(); 139 runAsShell( 140 PERMISSION_THREAD_NETWORK_PRIVILEGED, 141 () -> 142 mController.setEnabled( 143 enabled, directExecutor(), newOutcomeReceiver(future))); 144 future.get(SET_ENABLED_TIMEOUT.toSeconds(), SECONDS); 145 } 146 147 /** Joins the given network and wait for this device to become attached. */ joinAndWait(ActiveOperationalDataset activeDataset)148 public void joinAndWait(ActiveOperationalDataset activeDataset) 149 throws InterruptedException, ExecutionException, TimeoutException { 150 CompletableFuture<Void> future = new CompletableFuture<>(); 151 runAsShell( 152 PERMISSION_THREAD_NETWORK_PRIVILEGED, 153 () -> 154 mController.join( 155 activeDataset, directExecutor(), newOutcomeReceiver(future))); 156 future.get(JOIN_TIMEOUT.toSeconds(), SECONDS); 157 } 158 159 /** An synchronous variant of {@link ThreadNetworkController#leave}. */ leaveAndWait()160 public void leaveAndWait() throws InterruptedException, ExecutionException, TimeoutException { 161 CompletableFuture<Void> future = new CompletableFuture<>(); 162 runAsShell( 163 PERMISSION_THREAD_NETWORK_PRIVILEGED, 164 () -> mController.leave(directExecutor(), future::complete)); 165 future.get(LEAVE_TIMEOUT.toSeconds(), SECONDS); 166 } 167 168 /** Waits for the device role to become {@code deviceRole}. */ waitForRole(int deviceRole, Duration timeout)169 public int waitForRole(int deviceRole, Duration timeout) 170 throws InterruptedException, ExecutionException, TimeoutException { 171 return waitForRoleAnyOf(List.of(deviceRole), timeout); 172 } 173 174 /** Waits for the device role to become one of the values specified in {@code deviceRoles}. */ waitForRoleAnyOf(List<Integer> deviceRoles, Duration timeout)175 public int waitForRoleAnyOf(List<Integer> deviceRoles, Duration timeout) 176 throws InterruptedException, ExecutionException, TimeoutException { 177 CompletableFuture<Integer> future = new CompletableFuture<>(); 178 ThreadNetworkController.StateCallback callback = 179 newRole -> { 180 if (deviceRoles.contains(newRole)) { 181 future.complete(newRole); 182 } 183 }; 184 185 runAsShell( 186 ACCESS_NETWORK_STATE, 187 () -> mController.registerStateCallback(directExecutor(), callback)); 188 189 try { 190 return future.get(timeout.toSeconds(), SECONDS); 191 } finally { 192 mController.unregisterStateCallback(callback); 193 } 194 } 195 196 /** An synchronous variant of {@link ThreadNetworkController#setTestNetworkAsUpstream}. */ setTestNetworkAsUpstreamAndWait(@ullable String networkInterfaceName)197 public void setTestNetworkAsUpstreamAndWait(@Nullable String networkInterfaceName) 198 throws InterruptedException, ExecutionException, TimeoutException { 199 CompletableFuture<Void> future = new CompletableFuture<>(); 200 runAsShell( 201 PERMISSION_THREAD_NETWORK_PRIVILEGED, 202 NETWORK_SETTINGS, 203 () -> { 204 mController.setTestNetworkAsUpstream( 205 networkInterfaceName, directExecutor(), future::complete); 206 }); 207 future.get(CALLBACK_TIMEOUT.toSeconds(), SECONDS); 208 } 209 getConfiguration()210 public ThreadConfiguration getConfiguration() throws Exception { 211 CompletableFuture<ThreadConfiguration> future = new CompletableFuture<>(); 212 Consumer<ThreadConfiguration> callback = future::complete; 213 runAsShell( 214 PERMISSION_THREAD_NETWORK_PRIVILEGED, 215 () -> mController.registerConfigurationCallback(directExecutor(), callback)); 216 future.get(CONFIG_TIMEOUT.toSeconds(), SECONDS); 217 runAsShell( 218 PERMISSION_THREAD_NETWORK_PRIVILEGED, 219 () -> mController.unregisterConfigurationCallback(callback)); 220 return future.getNow(null); 221 } 222 setConfigurationAndWait(ThreadConfiguration config)223 public void setConfigurationAndWait(ThreadConfiguration config) throws Exception { 224 CompletableFuture<Void> future = new CompletableFuture<>(); 225 runAsShell( 226 PERMISSION_THREAD_NETWORK_PRIVILEGED, 227 () -> 228 mController.setConfiguration( 229 config, directExecutor(), newOutcomeReceiver(future))); 230 future.get(CONFIG_TIMEOUT.toSeconds(), SECONDS); 231 } 232 setNat64EnabledAndWait(boolean enabled)233 public void setNat64EnabledAndWait(boolean enabled) throws Exception { 234 final ThreadConfiguration config = getConfiguration(); 235 final ThreadConfiguration newConfig = 236 new ThreadConfiguration.Builder(config).setNat64Enabled(enabled).build(); 237 setConfigurationAndWait(newConfig); 238 } 239 newOutcomeReceiver( CompletableFuture<V> future)240 private static <V> OutcomeReceiver<V, ThreadNetworkException> newOutcomeReceiver( 241 CompletableFuture<V> future) { 242 return new OutcomeReceiver<V, ThreadNetworkException>() { 243 @Override 244 public void onResult(V result) { 245 future.complete(result); 246 } 247 248 @Override 249 public void onError(ThreadNetworkException e) { 250 future.completeExceptionally(e); 251 } 252 }; 253 } 254 } 255