1 /* 2 * Copyright (C) 2023 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 17 package android.net.thread.cts; 18 19 import static android.Manifest.permission.ACCESS_NETWORK_STATE; 20 import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK; 21 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; 22 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; 23 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; 24 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; 25 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; 26 import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_CHILD; 27 import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_LEADER; 28 import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_ROUTER; 29 import static android.net.thread.ThreadNetworkController.DEVICE_ROLE_STOPPED; 30 import static android.net.thread.ThreadNetworkController.EPHEMERAL_KEY_DISABLED; 31 import static android.net.thread.ThreadNetworkController.EPHEMERAL_KEY_ENABLED; 32 import static android.net.thread.ThreadNetworkController.STATE_DISABLED; 33 import static android.net.thread.ThreadNetworkController.STATE_DISABLING; 34 import static android.net.thread.ThreadNetworkController.STATE_ENABLED; 35 import static android.net.thread.ThreadNetworkController.THREAD_VERSION_1_3; 36 import static android.net.thread.ThreadNetworkException.ERROR_ABORTED; 37 import static android.net.thread.ThreadNetworkException.ERROR_BUSY; 38 import static android.net.thread.ThreadNetworkException.ERROR_FAILED_PRECONDITION; 39 import static android.net.thread.ThreadNetworkException.ERROR_REJECTED_BY_PEER; 40 import static android.net.thread.ThreadNetworkException.ERROR_THREAD_DISABLED; 41 import static android.net.thread.ThreadNetworkException.ERROR_UNSUPPORTED_FEATURE; 42 43 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 44 45 import static com.android.testutils.TestPermissionUtil.runAsShell; 46 47 import static com.google.common.truth.Truth.assertThat; 48 import static com.google.common.util.concurrent.MoreExecutors.directExecutor; 49 50 import static org.junit.Assert.assertThrows; 51 import static org.junit.Assert.fail; 52 import static org.junit.Assume.assumeFalse; 53 import static org.junit.Assume.assumeTrue; 54 55 import static java.util.concurrent.TimeUnit.MILLISECONDS; 56 57 import android.content.Context; 58 import android.net.ConnectivityManager; 59 import android.net.Network; 60 import android.net.NetworkCapabilities; 61 import android.net.NetworkRequest; 62 import android.net.nsd.NsdManager; 63 import android.net.nsd.NsdServiceInfo; 64 import android.net.thread.ActiveOperationalDataset; 65 import android.net.thread.OperationalDatasetTimestamp; 66 import android.net.thread.PendingOperationalDataset; 67 import android.net.thread.ThreadConfiguration; 68 import android.net.thread.ThreadNetworkController; 69 import android.net.thread.ThreadNetworkController.OperationalDatasetCallback; 70 import android.net.thread.ThreadNetworkController.StateCallback; 71 import android.net.thread.ThreadNetworkException; 72 import android.net.thread.ThreadNetworkManager; 73 import android.net.thread.utils.TapTestNetworkTracker; 74 import android.net.thread.utils.ThreadFeatureCheckerRule; 75 import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature; 76 import android.os.Build; 77 import android.os.HandlerThread; 78 import android.os.OutcomeReceiver; 79 import android.platform.test.annotations.RequiresFlagsEnabled; 80 import android.platform.test.flag.junit.CheckFlagsRule; 81 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 82 import android.util.SparseIntArray; 83 84 import androidx.annotation.NonNull; 85 import androidx.test.core.app.ApplicationProvider; 86 import androidx.test.filters.LargeTest; 87 88 import com.android.net.module.util.ArrayTrackRecord; 89 import com.android.net.thread.flags.Flags; 90 import com.android.testutils.FunctionalUtils.ThrowingRunnable; 91 92 import kotlin.Triple; 93 94 import org.junit.After; 95 import org.junit.Before; 96 import org.junit.Ignore; 97 import org.junit.Rule; 98 import org.junit.Test; 99 import org.junit.runner.RunWith; 100 import org.junit.runners.Parameterized; 101 102 import java.nio.charset.StandardCharsets; 103 import java.time.Duration; 104 import java.time.Instant; 105 import java.util.ArrayList; 106 import java.util.Arrays; 107 import java.util.Collection; 108 import java.util.HashSet; 109 import java.util.List; 110 import java.util.Map; 111 import java.util.Objects; 112 import java.util.Random; 113 import java.util.Set; 114 import java.util.concurrent.CompletableFuture; 115 import java.util.concurrent.ExecutionException; 116 import java.util.concurrent.Executor; 117 import java.util.concurrent.ExecutorService; 118 import java.util.concurrent.Executors; 119 import java.util.concurrent.TimeoutException; 120 import java.util.function.Consumer; 121 import java.util.function.Predicate; 122 123 /** CTS tests for {@link ThreadNetworkController}. */ 124 @LargeTest 125 @RequiresThreadFeature 126 @RunWith(Parameterized.class) 127 public class ThreadNetworkControllerTest { 128 private static final int JOIN_TIMEOUT_MILLIS = 30 * 1000; 129 private static final int LEAVE_TIMEOUT_MILLIS = 2_000; 130 private static final int MIGRATION_TIMEOUT_MILLIS = 40 * 1_000; 131 private static final int NETWORK_CALLBACK_TIMEOUT_MILLIS = 10 * 1000; 132 private static final int CALLBACK_TIMEOUT_MILLIS = 1_000; 133 private static final int ENABLED_TIMEOUT_MILLIS = 2_000; 134 private static final int SET_CONFIGURATION_TIMEOUT_MILLIS = 1_000; 135 private static final int SERVICE_DISCOVERY_TIMEOUT_MILLIS = 30_000; 136 private static final int SERVICE_LOST_TIMEOUT_MILLIS = 20_000; 137 private static final int VALID_POWER = 32_767; 138 private static final int VALID_CHANNEL = 20; 139 private static final int INVALID_CHANNEL = 10; 140 private static final String MESHCOP_SERVICE_TYPE = "_meshcop._udp"; 141 private static final String THREAD_NETWORK_PRIVILEGED = 142 "android.permission.THREAD_NETWORK_PRIVILEGED"; 143 private static final SparseIntArray CHANNEL_MAX_POWERS = 144 new SparseIntArray() { 145 { 146 put(VALID_CHANNEL, VALID_POWER); 147 } 148 }; 149 private static final Duration EPHEMERAL_KEY_LIFETIME = Duration.ofSeconds(1); 150 151 @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule(); 152 153 @Rule 154 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 155 156 private final Context mContext = ApplicationProvider.getApplicationContext(); 157 private ExecutorService mExecutor; 158 private ThreadNetworkController mController; 159 private NsdManager mNsdManager; 160 161 private Set<String> mGrantedPermissions; 162 private HandlerThread mHandlerThread; 163 private TapTestNetworkTracker mTestNetworkTracker; 164 165 private final List<Consumer<ThreadConfiguration>> mConfigurationCallbacksToCleanUp = 166 new ArrayList<>(); 167 168 public final boolean mIsBorderRouterEnabled; 169 private final ThreadConfiguration mDefaultConfig; 170 171 @Parameterized.Parameters configArguments()172 public static Collection configArguments() { 173 return Arrays.asList(new Object[][] {{false}, {true}}); 174 } 175 ThreadNetworkControllerTest(boolean isBorderRouterEnabled)176 public ThreadNetworkControllerTest(boolean isBorderRouterEnabled) { 177 mIsBorderRouterEnabled = isBorderRouterEnabled; 178 mDefaultConfig = 179 new ThreadConfiguration.Builder() 180 .setBorderRouterEnabled(isBorderRouterEnabled) 181 .build(); 182 } 183 184 @Before setUp()185 public void setUp() throws Exception { 186 mController = 187 mContext.getSystemService(ThreadNetworkManager.class) 188 .getAllThreadNetworkControllers() 189 .get(0); 190 191 mGrantedPermissions = new HashSet<String>(); 192 mExecutor = Executors.newSingleThreadExecutor(); 193 mNsdManager = mContext.getSystemService(NsdManager.class); 194 mHandlerThread = new HandlerThread(this.getClass().getSimpleName()); 195 mHandlerThread.start(); 196 197 setEnabledAndWait(mController, true); 198 setConfigurationAndWait(mController, mDefaultConfig); 199 if (mDefaultConfig.isBorderRouterEnabled()) { 200 deactivateEphemeralKeyModeAndWait(mController); 201 } 202 } 203 204 @After tearDown()205 public void tearDown() throws Exception { 206 dropAllPermissions(); 207 setEnabledAndWait(mController, true); 208 leaveAndWait(mController); 209 tearDownTestNetwork(); 210 setConfigurationAndWait(mController, mDefaultConfig); 211 for (Consumer<ThreadConfiguration> configurationCallback : 212 mConfigurationCallbacksToCleanUp) { 213 try { 214 runAsShell( 215 THREAD_NETWORK_PRIVILEGED, 216 () -> mController.unregisterConfigurationCallback(configurationCallback)); 217 } catch (IllegalArgumentException e) { 218 // Ignore the exception when the callback is not registered. 219 } 220 } 221 mConfigurationCallbacksToCleanUp.clear(); 222 if (mDefaultConfig.isBorderRouterEnabled()) { 223 deactivateEphemeralKeyModeAndWait(mController); 224 } 225 } 226 227 @Test getThreadVersion_returnsAtLeastThreadVersion1P3()228 public void getThreadVersion_returnsAtLeastThreadVersion1P3() { 229 assertThat(mController.getThreadVersion()).isAtLeast(THREAD_VERSION_1_3); 230 } 231 232 @Test registerStateCallback_permissionsGranted_returnsCurrentStates()233 public void registerStateCallback_permissionsGranted_returnsCurrentStates() throws Exception { 234 CompletableFuture<Integer> deviceRole = new CompletableFuture<>(); 235 StateCallback callback = deviceRole::complete; 236 237 try { 238 runAsShell( 239 ACCESS_NETWORK_STATE, 240 () -> mController.registerStateCallback(mExecutor, callback)); 241 242 assertThat(deviceRole.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS)) 243 .isEqualTo(DEVICE_ROLE_STOPPED); 244 } finally { 245 runAsShell(ACCESS_NETWORK_STATE, () -> mController.unregisterStateCallback(callback)); 246 } 247 } 248 249 @Test subscribeThreadEnableState_getActiveDataset_onThreadEnableStateChangedNotCalled()250 public void subscribeThreadEnableState_getActiveDataset_onThreadEnableStateChangedNotCalled() 251 throws Exception { 252 EnabledStateListener listener = new EnabledStateListener(mController); 253 listener.expectThreadEnabledState(STATE_ENABLED); 254 255 getActiveOperationalDataset(mController); 256 257 listener.expectCallbackNotCalled(); 258 } 259 260 @Test registerStateCallback_returnsUpdatedEnabledStates()261 public void registerStateCallback_returnsUpdatedEnabledStates() throws Exception { 262 CompletableFuture<Void> setFuture1 = new CompletableFuture<>(); 263 CompletableFuture<Void> setFuture2 = new CompletableFuture<>(); 264 EnabledStateListener listener = new EnabledStateListener(mController); 265 266 try { 267 runAsShell( 268 THREAD_NETWORK_PRIVILEGED, 269 () -> { 270 mController.setEnabled(false, mExecutor, newOutcomeReceiver(setFuture1)); 271 }); 272 setFuture1.get(ENABLED_TIMEOUT_MILLIS, MILLISECONDS); 273 274 runAsShell( 275 THREAD_NETWORK_PRIVILEGED, 276 () -> { 277 mController.setEnabled(true, mExecutor, newOutcomeReceiver(setFuture2)); 278 }); 279 setFuture2.get(ENABLED_TIMEOUT_MILLIS, MILLISECONDS); 280 281 listener.expectThreadEnabledState(STATE_ENABLED); 282 listener.expectThreadEnabledState(STATE_DISABLING); 283 listener.expectThreadEnabledState(STATE_DISABLED); 284 listener.expectThreadEnabledState(STATE_ENABLED); 285 } finally { 286 listener.unregisterStateCallback(); 287 } 288 } 289 290 @Test registerStateCallback_noPermissions_throwsSecurityException()291 public void registerStateCallback_noPermissions_throwsSecurityException() throws Exception { 292 dropAllPermissions(); 293 294 assertThrows( 295 SecurityException.class, 296 () -> mController.registerStateCallback(mExecutor, role -> {})); 297 } 298 299 @Test registerStateCallback_alreadyRegistered_throwsIllegalArgumentException()300 public void registerStateCallback_alreadyRegistered_throwsIllegalArgumentException() 301 throws Exception { 302 grantPermissions(ACCESS_NETWORK_STATE); 303 CompletableFuture<Integer> deviceRole = new CompletableFuture<>(); 304 StateCallback callback = role -> deviceRole.complete(role); 305 306 mController.registerStateCallback(mExecutor, callback); 307 308 assertThrows( 309 IllegalArgumentException.class, 310 () -> mController.registerStateCallback(mExecutor, callback)); 311 } 312 313 @Test unregisterStateCallback_noPermissions_throwsSecurityException()314 public void unregisterStateCallback_noPermissions_throwsSecurityException() throws Exception { 315 CompletableFuture<Integer> deviceRole = new CompletableFuture<>(); 316 StateCallback callback = role -> deviceRole.complete(role); 317 runAsShell( 318 ACCESS_NETWORK_STATE, () -> mController.registerStateCallback(mExecutor, callback)); 319 320 try { 321 dropAllPermissions(); 322 assertThrows( 323 SecurityException.class, () -> mController.unregisterStateCallback(callback)); 324 } finally { 325 runAsShell(ACCESS_NETWORK_STATE, () -> mController.unregisterStateCallback(callback)); 326 } 327 } 328 329 @Test unregisterStateCallback_callbackRegistered_success()330 public void unregisterStateCallback_callbackRegistered_success() throws Exception { 331 grantPermissions(ACCESS_NETWORK_STATE); 332 CompletableFuture<Integer> deviceRole = new CompletableFuture<>(); 333 StateCallback callback = role -> deviceRole.complete(role); 334 335 assertDoesNotThrow(() -> mController.registerStateCallback(mExecutor, callback)); 336 mController.unregisterStateCallback(callback); 337 } 338 339 @Test unregisterStateCallback_callbackNotRegistered_throwsIllegalArgumentException()340 public void unregisterStateCallback_callbackNotRegistered_throwsIllegalArgumentException() 341 throws Exception { 342 CompletableFuture<Integer> deviceRole = new CompletableFuture<>(); 343 StateCallback callback = role -> deviceRole.complete(role); 344 345 assertThrows( 346 IllegalArgumentException.class, 347 () -> mController.unregisterStateCallback(callback)); 348 } 349 350 @Test unregisterStateCallback_alreadyUnregistered_throwsIllegalArgumentException()351 public void unregisterStateCallback_alreadyUnregistered_throwsIllegalArgumentException() 352 throws Exception { 353 grantPermissions(ACCESS_NETWORK_STATE); 354 CompletableFuture<Integer> deviceRole = new CompletableFuture<>(); 355 StateCallback callback = deviceRole::complete; 356 mController.registerStateCallback(mExecutor, callback); 357 mController.unregisterStateCallback(callback); 358 359 assertThrows( 360 IllegalArgumentException.class, 361 () -> mController.unregisterStateCallback(callback)); 362 } 363 364 @Test registerOperationalDatasetCallback_permissionsGranted_returnsCurrentStates()365 public void registerOperationalDatasetCallback_permissionsGranted_returnsCurrentStates() 366 throws Exception { 367 grantPermissions(ACCESS_NETWORK_STATE, THREAD_NETWORK_PRIVILEGED); 368 CompletableFuture<ActiveOperationalDataset> activeFuture = new CompletableFuture<>(); 369 CompletableFuture<PendingOperationalDataset> pendingFuture = new CompletableFuture<>(); 370 var callback = newDatasetCallback(activeFuture, pendingFuture); 371 372 try { 373 mController.registerOperationalDatasetCallback(mExecutor, callback); 374 375 assertThat(activeFuture.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS)).isNull(); 376 assertThat(pendingFuture.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS)).isNull(); 377 } finally { 378 mController.unregisterOperationalDatasetCallback(callback); 379 } 380 } 381 382 @Test registerOperationalDatasetCallback_noPermissions_throwsSecurityException()383 public void registerOperationalDatasetCallback_noPermissions_throwsSecurityException() 384 throws Exception { 385 dropAllPermissions(); 386 CompletableFuture<ActiveOperationalDataset> activeFuture = new CompletableFuture<>(); 387 CompletableFuture<PendingOperationalDataset> pendingFuture = new CompletableFuture<>(); 388 var callback = newDatasetCallback(activeFuture, pendingFuture); 389 390 assertThrows( 391 SecurityException.class, 392 () -> mController.registerOperationalDatasetCallback(mExecutor, callback)); 393 } 394 395 @Test unregisterOperationalDatasetCallback_callbackRegistered_success()396 public void unregisterOperationalDatasetCallback_callbackRegistered_success() throws Exception { 397 grantPermissions(ACCESS_NETWORK_STATE, THREAD_NETWORK_PRIVILEGED); 398 CompletableFuture<ActiveOperationalDataset> activeFuture = new CompletableFuture<>(); 399 CompletableFuture<PendingOperationalDataset> pendingFuture = new CompletableFuture<>(); 400 var callback = newDatasetCallback(activeFuture, pendingFuture); 401 mController.registerOperationalDatasetCallback(mExecutor, callback); 402 403 assertDoesNotThrow(() -> mController.unregisterOperationalDatasetCallback(callback)); 404 } 405 406 @Test unregisterOperationalDatasetCallback_noPermissions_throwsSecurityException()407 public void unregisterOperationalDatasetCallback_noPermissions_throwsSecurityException() 408 throws Exception { 409 CompletableFuture<ActiveOperationalDataset> activeFuture = new CompletableFuture<>(); 410 CompletableFuture<PendingOperationalDataset> pendingFuture = new CompletableFuture<>(); 411 var callback = newDatasetCallback(activeFuture, pendingFuture); 412 runAsShell( 413 ACCESS_NETWORK_STATE, 414 THREAD_NETWORK_PRIVILEGED, 415 () -> mController.registerOperationalDatasetCallback(mExecutor, callback)); 416 417 try { 418 dropAllPermissions(); 419 assertThrows( 420 SecurityException.class, 421 () -> mController.unregisterOperationalDatasetCallback(callback)); 422 } finally { 423 runAsShell( 424 ACCESS_NETWORK_STATE, 425 THREAD_NETWORK_PRIVILEGED, 426 () -> mController.unregisterOperationalDatasetCallback(callback)); 427 } 428 } 429 newOutcomeReceiver( CompletableFuture<V> future)430 private static <V> OutcomeReceiver<V, ThreadNetworkException> newOutcomeReceiver( 431 CompletableFuture<V> future) { 432 return new OutcomeReceiver<V, ThreadNetworkException>() { 433 @Override 434 public void onResult(V result) { 435 future.complete(result); 436 } 437 438 @Override 439 public void onError(ThreadNetworkException e) { 440 future.completeExceptionally(e); 441 } 442 }; 443 } 444 445 @Test 446 public void join_withPrivilegedPermission_success() throws Exception { 447 ActiveOperationalDataset activeDataset = newRandomizedDataset("TestNet", mController); 448 CompletableFuture<Void> joinFuture = new CompletableFuture<>(); 449 450 runAsShell( 451 THREAD_NETWORK_PRIVILEGED, 452 () -> mController.join(activeDataset, mExecutor, newOutcomeReceiver(joinFuture))); 453 joinFuture.get(JOIN_TIMEOUT_MILLIS, MILLISECONDS); 454 455 assertThat(isAttached(mController)).isTrue(); 456 assertThat(getActiveOperationalDataset(mController)).isEqualTo(activeDataset); 457 } 458 459 @Test 460 public void join_withoutPrivilegedPermission_throwsSecurityException() throws Exception { 461 dropAllPermissions(); 462 ActiveOperationalDataset activeDataset = newRandomizedDataset("TestNet", mController); 463 464 assertThrows( 465 SecurityException.class, () -> mController.join(activeDataset, mExecutor, v -> {})); 466 } 467 468 @Test 469 public void join_threadDisabled_failsWithErrorThreadDisabled() throws Exception { 470 setEnabledAndWait(mController, false); 471 ActiveOperationalDataset activeDataset = newRandomizedDataset("TestNet", mController); 472 CompletableFuture<Void> joinFuture = new CompletableFuture<>(); 473 474 runAsShell( 475 THREAD_NETWORK_PRIVILEGED, 476 () -> mController.join(activeDataset, mExecutor, newOutcomeReceiver(joinFuture))); 477 478 var thrown = 479 assertThrows( 480 ExecutionException.class, 481 () -> joinFuture.get(JOIN_TIMEOUT_MILLIS, MILLISECONDS)); 482 var threadException = (ThreadNetworkException) thrown.getCause(); 483 assertThat(threadException.getErrorCode()).isEqualTo(ERROR_THREAD_DISABLED); 484 } 485 486 @Test 487 public void join_concurrentRequests_firstOneIsAborted() throws Exception { 488 final byte[] KEY_1 = new byte[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; 489 final byte[] KEY_2 = new byte[] {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; 490 ActiveOperationalDataset activeDataset1 = 491 new ActiveOperationalDataset.Builder(newRandomizedDataset("TestNet", mController)) 492 .setNetworkKey(KEY_1) 493 .build(); 494 ActiveOperationalDataset activeDataset2 = 495 new ActiveOperationalDataset.Builder(activeDataset1).setNetworkKey(KEY_2).build(); 496 CompletableFuture<Void> joinFuture1 = new CompletableFuture<>(); 497 CompletableFuture<Void> joinFuture2 = new CompletableFuture<>(); 498 499 runAsShell( 500 THREAD_NETWORK_PRIVILEGED, 501 () -> { 502 mController.join(activeDataset1, mExecutor, newOutcomeReceiver(joinFuture1)); 503 mController.join(activeDataset2, mExecutor, newOutcomeReceiver(joinFuture2)); 504 }); 505 506 var thrown = 507 assertThrows( 508 ExecutionException.class, 509 () -> joinFuture1.get(JOIN_TIMEOUT_MILLIS, MILLISECONDS)); 510 var threadException = (ThreadNetworkException) thrown.getCause(); 511 assertThat(threadException.getErrorCode()).isEqualTo(ERROR_ABORTED); 512 joinFuture2.get(JOIN_TIMEOUT_MILLIS, MILLISECONDS); 513 assertThat(isAttached(mController)).isTrue(); 514 assertThat(getActiveOperationalDataset(mController)).isEqualTo(activeDataset2); 515 } 516 517 @Test 518 public void leave_withPrivilegedPermission_success() throws Exception { 519 CompletableFuture<Void> leaveFuture = new CompletableFuture<>(); 520 joinRandomizedDatasetAndWait(mController); 521 522 runAsShell( 523 THREAD_NETWORK_PRIVILEGED, 524 () -> mController.leave(mExecutor, newOutcomeReceiver(leaveFuture))); 525 leaveFuture.get(LEAVE_TIMEOUT_MILLIS, MILLISECONDS); 526 527 assertThat(getDeviceRole(mController)).isEqualTo(DEVICE_ROLE_STOPPED); 528 } 529 530 @Test 531 public void leave_withoutPrivilegedPermission_throwsSecurityException() { 532 dropAllPermissions(); 533 534 assertThrows(SecurityException.class, () -> mController.leave(mExecutor, v -> {})); 535 } 536 537 @Test 538 public void leave_threadDisabled_success() throws Exception { 539 setEnabledAndWait(mController, false); 540 CompletableFuture<Void> leaveFuture = new CompletableFuture<>(); 541 542 leave(mController, newOutcomeReceiver(leaveFuture)); 543 leaveFuture.get(LEAVE_TIMEOUT_MILLIS, MILLISECONDS); 544 545 assertThat(getDeviceRole(mController)).isEqualTo(DEVICE_ROLE_STOPPED); 546 } 547 548 @Test 549 public void leave_concurrentRequests_bothSuccess() throws Exception { 550 CompletableFuture<Void> leaveFuture1 = new CompletableFuture<>(); 551 CompletableFuture<Void> leaveFuture2 = new CompletableFuture<>(); 552 joinRandomizedDatasetAndWait(mController); 553 554 runAsShell( 555 THREAD_NETWORK_PRIVILEGED, 556 () -> { 557 mController.leave(mExecutor, newOutcomeReceiver(leaveFuture1)); 558 mController.leave(mExecutor, newOutcomeReceiver(leaveFuture2)); 559 }); 560 561 leaveFuture1.get(LEAVE_TIMEOUT_MILLIS, MILLISECONDS); 562 leaveFuture2.get(LEAVE_TIMEOUT_MILLIS, MILLISECONDS); 563 assertThat(getDeviceRole(mController)).isEqualTo(DEVICE_ROLE_STOPPED); 564 } 565 566 @Test 567 public void scheduleMigration_withPrivilegedPermission_newDatasetApplied() throws Exception { 568 grantPermissions(ACCESS_NETWORK_STATE, THREAD_NETWORK_PRIVILEGED); 569 ActiveOperationalDataset activeDataset1 = 570 new ActiveOperationalDataset.Builder(newRandomizedDataset("TestNet", mController)) 571 .setActiveTimestamp(new OperationalDatasetTimestamp(1L, 0, false)) 572 .setExtendedPanId(new byte[] {1, 1, 1, 1, 1, 1, 1, 1}) 573 .build(); 574 ActiveOperationalDataset activeDataset2 = 575 new ActiveOperationalDataset.Builder(activeDataset1) 576 .setActiveTimestamp(new OperationalDatasetTimestamp(2L, 0, false)) 577 .setNetworkName("ThreadNet2") 578 .build(); 579 PendingOperationalDataset pendingDataset2 = 580 new PendingOperationalDataset( 581 activeDataset2, 582 OperationalDatasetTimestamp.fromInstant(Instant.now()), 583 Duration.ofSeconds(30)); 584 CompletableFuture<Void> joinFuture = new CompletableFuture<>(); 585 CompletableFuture<Void> migrateFuture = new CompletableFuture<>(); 586 mController.join(activeDataset1, mExecutor, newOutcomeReceiver(joinFuture)); 587 joinFuture.get(JOIN_TIMEOUT_MILLIS, MILLISECONDS); 588 589 mController.scheduleMigration( 590 pendingDataset2, mExecutor, newOutcomeReceiver(migrateFuture)); 591 migrateFuture.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS); 592 593 CompletableFuture<Boolean> dataset2IsApplied = new CompletableFuture<>(); 594 CompletableFuture<Boolean> pendingDatasetIsRemoved = new CompletableFuture<>(); 595 OperationalDatasetCallback datasetCallback = 596 new OperationalDatasetCallback() { 597 @Override 598 public void onActiveOperationalDatasetChanged( 599 ActiveOperationalDataset activeDataset) { 600 if (Objects.equals(activeDataset, activeDataset2)) { 601 dataset2IsApplied.complete(true); 602 } 603 } 604 605 @Override 606 public void onPendingOperationalDatasetChanged( 607 PendingOperationalDataset pendingDataset) { 608 if (pendingDataset == null) { 609 pendingDatasetIsRemoved.complete(true); 610 } 611 } 612 }; 613 mController.registerOperationalDatasetCallback(directExecutor(), datasetCallback); 614 try { 615 assertThat(dataset2IsApplied.get(MIGRATION_TIMEOUT_MILLIS, MILLISECONDS)).isTrue(); 616 assertThat(pendingDatasetIsRemoved.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS)).isTrue(); 617 } finally { 618 mController.unregisterOperationalDatasetCallback(datasetCallback); 619 } 620 } 621 622 @Test 623 public void scheduleMigration_whenNotAttached_failWithPreconditionError() throws Exception { 624 grantPermissions(ACCESS_NETWORK_STATE, THREAD_NETWORK_PRIVILEGED); 625 PendingOperationalDataset pendingDataset = 626 new PendingOperationalDataset( 627 newRandomizedDataset("TestNet", mController), 628 OperationalDatasetTimestamp.fromInstant(Instant.now()), 629 Duration.ofSeconds(30)); 630 CompletableFuture<Void> migrateFuture = new CompletableFuture<>(); 631 632 mController.scheduleMigration(pendingDataset, mExecutor, newOutcomeReceiver(migrateFuture)); 633 634 ThreadNetworkException thrown = 635 (ThreadNetworkException) 636 assertThrows(ExecutionException.class, migrateFuture::get).getCause(); 637 assertThat(thrown.getErrorCode()).isEqualTo(ERROR_FAILED_PRECONDITION); 638 } 639 640 @Test 641 public void scheduleMigration_secondRequestHasSmallerTimestamp_rejectedByLeader() 642 throws Exception { 643 grantPermissions(ACCESS_NETWORK_STATE, THREAD_NETWORK_PRIVILEGED); 644 final ActiveOperationalDataset activeDataset = 645 new ActiveOperationalDataset.Builder(newRandomizedDataset("testNet", mController)) 646 .setActiveTimestamp(new OperationalDatasetTimestamp(1L, 0, false)) 647 .build(); 648 ActiveOperationalDataset activeDataset1 = 649 new ActiveOperationalDataset.Builder(activeDataset) 650 .setActiveTimestamp(new OperationalDatasetTimestamp(2L, 0, false)) 651 .setNetworkName("testNet1") 652 .build(); 653 PendingOperationalDataset pendingDataset1 = 654 new PendingOperationalDataset( 655 activeDataset1, 656 new OperationalDatasetTimestamp(100, 0, false), 657 Duration.ofSeconds(30)); 658 ActiveOperationalDataset activeDataset2 = 659 new ActiveOperationalDataset.Builder(activeDataset) 660 .setActiveTimestamp(new OperationalDatasetTimestamp(3L, 0, false)) 661 .setNetworkName("testNet2") 662 .build(); 663 PendingOperationalDataset pendingDataset2 = 664 new PendingOperationalDataset( 665 activeDataset2, 666 new OperationalDatasetTimestamp(20, 0, false), 667 Duration.ofSeconds(30)); 668 CompletableFuture<Void> joinFuture = new CompletableFuture<>(); 669 CompletableFuture<Void> migrateFuture1 = new CompletableFuture<>(); 670 CompletableFuture<Void> migrateFuture2 = new CompletableFuture<>(); 671 mController.join(activeDataset, mExecutor, newOutcomeReceiver(joinFuture)); 672 joinFuture.get(JOIN_TIMEOUT_MILLIS, MILLISECONDS); 673 674 mController.scheduleMigration( 675 pendingDataset1, mExecutor, newOutcomeReceiver(migrateFuture1)); 676 migrateFuture1.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS); 677 mController.scheduleMigration( 678 pendingDataset2, mExecutor, newOutcomeReceiver(migrateFuture2)); 679 680 ThreadNetworkException thrown = 681 (ThreadNetworkException) 682 assertThrows(ExecutionException.class, migrateFuture2::get).getCause(); 683 assertThat(thrown.getErrorCode()).isEqualTo(ERROR_REJECTED_BY_PEER); 684 } 685 686 @Test 687 public void scheduleMigration_secondRequestHasLargerTimestamp_newDatasetApplied() 688 throws Exception { 689 grantPermissions(ACCESS_NETWORK_STATE, THREAD_NETWORK_PRIVILEGED); 690 final ActiveOperationalDataset activeDataset = 691 new ActiveOperationalDataset.Builder(newRandomizedDataset("validName", mController)) 692 .setActiveTimestamp(new OperationalDatasetTimestamp(1L, 0, false)) 693 .build(); 694 ActiveOperationalDataset activeDataset1 = 695 new ActiveOperationalDataset.Builder(activeDataset) 696 .setActiveTimestamp(new OperationalDatasetTimestamp(2L, 0, false)) 697 .setNetworkName("testNet1") 698 .build(); 699 PendingOperationalDataset pendingDataset1 = 700 new PendingOperationalDataset( 701 activeDataset1, 702 new OperationalDatasetTimestamp(100, 0, false), 703 Duration.ofSeconds(30)); 704 ActiveOperationalDataset activeDataset2 = 705 new ActiveOperationalDataset.Builder(activeDataset) 706 .setActiveTimestamp(new OperationalDatasetTimestamp(3L, 0, false)) 707 .setNetworkName("testNet2") 708 .build(); 709 PendingOperationalDataset pendingDataset2 = 710 new PendingOperationalDataset( 711 activeDataset2, 712 new OperationalDatasetTimestamp(200, 0, false), 713 Duration.ofSeconds(30)); 714 CompletableFuture<Void> joinFuture = new CompletableFuture<>(); 715 CompletableFuture<Void> migrateFuture1 = new CompletableFuture<>(); 716 CompletableFuture<Void> migrateFuture2 = new CompletableFuture<>(); 717 mController.join(activeDataset, mExecutor, newOutcomeReceiver(joinFuture)); 718 joinFuture.get(JOIN_TIMEOUT_MILLIS, MILLISECONDS); 719 720 mController.scheduleMigration( 721 pendingDataset1, mExecutor, newOutcomeReceiver(migrateFuture1)); 722 migrateFuture1.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS); 723 mController.scheduleMigration( 724 pendingDataset2, mExecutor, newOutcomeReceiver(migrateFuture2)); 725 migrateFuture2.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS); 726 727 CompletableFuture<Boolean> dataset2IsApplied = new CompletableFuture<>(); 728 CompletableFuture<Boolean> pendingDatasetIsRemoved = new CompletableFuture<>(); 729 OperationalDatasetCallback datasetCallback = 730 new OperationalDatasetCallback() { 731 @Override 732 public void onActiveOperationalDatasetChanged( 733 ActiveOperationalDataset activeDataset) { 734 if (activeDataset.equals(activeDataset2)) { 735 dataset2IsApplied.complete(true); 736 } 737 } 738 739 @Override 740 public void onPendingOperationalDatasetChanged( 741 PendingOperationalDataset pendingDataset) { 742 if (pendingDataset == null) { 743 pendingDatasetIsRemoved.complete(true); 744 } 745 } 746 }; 747 mController.registerOperationalDatasetCallback(directExecutor(), datasetCallback); 748 try { 749 assertThat(dataset2IsApplied.get(MIGRATION_TIMEOUT_MILLIS, MILLISECONDS)).isTrue(); 750 assertThat(pendingDatasetIsRemoved.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS)).isTrue(); 751 } finally { 752 mController.unregisterOperationalDatasetCallback(datasetCallback); 753 } 754 } 755 756 @Test 757 public void scheduleMigration_threadDisabled_failsWithErrorThreadDisabled() throws Exception { 758 ActiveOperationalDataset activeDataset = newRandomizedDataset("TestNet", mController); 759 PendingOperationalDataset pendingDataset = 760 new PendingOperationalDataset( 761 activeDataset, 762 OperationalDatasetTimestamp.fromInstant(Instant.now()), 763 Duration.ofSeconds(30)); 764 joinRandomizedDatasetAndWait(mController); 765 CompletableFuture<Void> migrationFuture = new CompletableFuture<>(); 766 767 setEnabledAndWait(mController, false); 768 769 scheduleMigration(mController, pendingDataset, newOutcomeReceiver(migrationFuture)); 770 771 ThreadNetworkException thrown = 772 (ThreadNetworkException) 773 assertThrows(ExecutionException.class, migrationFuture::get).getCause(); 774 assertThat(thrown.getErrorCode()).isEqualTo(ERROR_THREAD_DISABLED); 775 } 776 777 @Test 778 public void createRandomizedDataset_wrongNetworkNameLength_throwsIllegalArgumentException() { 779 assertThrows( 780 IllegalArgumentException.class, 781 () -> mController.createRandomizedDataset("", mExecutor, dataset -> {})); 782 783 assertThrows( 784 IllegalArgumentException.class, 785 () -> 786 mController.createRandomizedDataset( 787 "ANetNameIs17Bytes", mExecutor, dataset -> {})); 788 } 789 790 @Test 791 public void createRandomizedDataset_validNetworkName_success() throws Exception { 792 ActiveOperationalDataset dataset = newRandomizedDataset("validName", mController); 793 794 assertThat(dataset.getNetworkName()).isEqualTo("validName"); 795 assertThat(dataset.getPanId()).isLessThan(0xffff); 796 assertThat(dataset.getChannelMask().size()).isAtLeast(1); 797 assertThat(dataset.getExtendedPanId()).hasLength(8); 798 assertThat(dataset.getNetworkKey()).hasLength(16); 799 assertThat(dataset.getPskc()).hasLength(16); 800 assertThat(dataset.getMeshLocalPrefix().getPrefixLength()).isEqualTo(64); 801 assertThat(dataset.getMeshLocalPrefix().getRawAddress()[0]).isEqualTo((byte) 0xfd); 802 } 803 804 @Test 805 public void setEnabled_permissionsGranted_succeeds() throws Exception { 806 CompletableFuture<Void> setFuture1 = new CompletableFuture<>(); 807 CompletableFuture<Void> setFuture2 = new CompletableFuture<>(); 808 809 runAsShell( 810 THREAD_NETWORK_PRIVILEGED, 811 () -> mController.setEnabled(false, mExecutor, newOutcomeReceiver(setFuture1))); 812 setFuture1.get(ENABLED_TIMEOUT_MILLIS, MILLISECONDS); 813 waitForEnabledState(mController, booleanToEnabledState(false)); 814 815 runAsShell( 816 THREAD_NETWORK_PRIVILEGED, 817 () -> mController.setEnabled(true, mExecutor, newOutcomeReceiver(setFuture2))); 818 setFuture2.get(ENABLED_TIMEOUT_MILLIS, MILLISECONDS); 819 waitForEnabledState(mController, booleanToEnabledState(true)); 820 } 821 822 @Test 823 public void setEnabled_noPermissions_throwsSecurityException() throws Exception { 824 CompletableFuture<Void> setFuture = new CompletableFuture<>(); 825 assertThrows( 826 SecurityException.class, 827 () -> mController.setEnabled(false, mExecutor, newOutcomeReceiver(setFuture))); 828 } 829 830 @Test 831 public void setEnabled_disable_leavesThreadNetwork() throws Exception { 832 joinRandomizedDatasetAndWait(mController); 833 setEnabledAndWait(mController, false); 834 assertThat(getDeviceRole(mController)).isEqualTo(DEVICE_ROLE_STOPPED); 835 } 836 837 @Test 838 public void setEnabled_enableFollowedByDisable_allSucceed() throws Exception { 839 joinRandomizedDatasetAndWait(mController); 840 CompletableFuture<Void> setFuture1 = new CompletableFuture<>(); 841 CompletableFuture<Void> setFuture2 = new CompletableFuture<>(); 842 EnabledStateListener listener = new EnabledStateListener(mController); 843 listener.expectThreadEnabledState(STATE_ENABLED); 844 845 runAsShell( 846 THREAD_NETWORK_PRIVILEGED, 847 () -> { 848 mController.setEnabled(true, mExecutor, newOutcomeReceiver(setFuture1)); 849 mController.setEnabled(false, mExecutor, newOutcomeReceiver(setFuture2)); 850 }); 851 setFuture1.get(ENABLED_TIMEOUT_MILLIS, MILLISECONDS); 852 setFuture2.get(ENABLED_TIMEOUT_MILLIS, MILLISECONDS); 853 854 listener.expectThreadEnabledState(STATE_DISABLING); 855 listener.expectThreadEnabledState(STATE_DISABLED); 856 assertThat(getDeviceRole(mController)).isEqualTo(DEVICE_ROLE_STOPPED); 857 // FIXME: this is not called when a exception is thrown after the creation of `listener` 858 listener.unregisterStateCallback(); 859 } 860 861 @Test 862 @RequiresFlagsEnabled({Flags.FLAG_EPSKC_ENABLED}) 863 public void getMaxEphemeralKeyLifetime_isLargerThanZero() { 864 assertThat(mController.getMaxEphemeralKeyLifetime()).isGreaterThan(Duration.ZERO); 865 } 866 867 @Test 868 @RequiresFlagsEnabled({Flags.FLAG_EPSKC_ENABLED}) 869 public void activateEphemeralKeyMode_withPrivilegedPermission_succeeds() throws Exception { 870 assumeTrue(mDefaultConfig.isBorderRouterEnabled()); 871 joinRandomizedDatasetAndWait(mController); 872 CompletableFuture<Void> startFuture = new CompletableFuture<>(); 873 874 runAsShell( 875 THREAD_NETWORK_PRIVILEGED, 876 () -> 877 mController.activateEphemeralKeyMode( 878 EPHEMERAL_KEY_LIFETIME, 879 mExecutor, 880 newOutcomeReceiver(startFuture))); 881 882 startFuture.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS); 883 } 884 885 @Test 886 @RequiresFlagsEnabled({Flags.FLAG_EPSKC_ENABLED}) 887 public void activateEphemeralKeyMode_withoutPrivilegedPermission_throwsSecurityException() 888 throws Exception { 889 assumeTrue(mDefaultConfig.isBorderRouterEnabled()); 890 dropAllPermissions(); 891 892 assertThrows( 893 SecurityException.class, 894 () -> 895 mController.activateEphemeralKeyMode( 896 EPHEMERAL_KEY_LIFETIME, mExecutor, v -> {})); 897 } 898 899 @Test 900 @RequiresFlagsEnabled({Flags.FLAG_EPSKC_ENABLED}) 901 public void activateEphemeralKeyMode_withZeroLifetime_throwsIllegalArgumentException() 902 throws Exception { 903 assumeTrue(mDefaultConfig.isBorderRouterEnabled()); 904 grantPermissions(THREAD_NETWORK_PRIVILEGED); 905 906 assertThrows( 907 IllegalArgumentException.class, 908 () -> mController.activateEphemeralKeyMode(Duration.ZERO, mExecutor, v -> {})); 909 } 910 911 @Test 912 @RequiresFlagsEnabled({Flags.FLAG_EPSKC_ENABLED}) 913 public void activateEphemeralKeyMode_withInvalidLargeLifetime_throwsIllegalArgumentException() 914 throws Exception { 915 assumeTrue(mDefaultConfig.isBorderRouterEnabled()); 916 grantPermissions(THREAD_NETWORK_PRIVILEGED); 917 Duration lifetime = mController.getMaxEphemeralKeyLifetime().plusMillis(1); 918 919 assertThrows( 920 IllegalArgumentException.class, 921 () -> mController.activateEphemeralKeyMode(lifetime, Runnable::run, v -> {})); 922 } 923 924 @Test 925 @RequiresFlagsEnabled({Flags.FLAG_EPSKC_ENABLED}) 926 public void activateEphemeralKeyMode_concurrentRequests_secondOneFailsWithBusyError() 927 throws Exception { 928 assumeTrue(mDefaultConfig.isBorderRouterEnabled()); 929 joinRandomizedDatasetAndWait(mController); 930 CompletableFuture<Void> future1 = new CompletableFuture<>(); 931 CompletableFuture<Void> future2 = new CompletableFuture<>(); 932 933 runAsShell( 934 THREAD_NETWORK_PRIVILEGED, 935 () -> { 936 mController.activateEphemeralKeyMode( 937 EPHEMERAL_KEY_LIFETIME, mExecutor, newOutcomeReceiver(future1)); 938 mController.activateEphemeralKeyMode( 939 EPHEMERAL_KEY_LIFETIME, mExecutor, newOutcomeReceiver(future2)); 940 }); 941 942 var thrown = 943 assertThrows( 944 ExecutionException.class, 945 () -> { 946 future2.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS); 947 }); 948 var threadException = (ThreadNetworkException) thrown.getCause(); 949 assertThat(threadException.getErrorCode()).isEqualTo(ERROR_BUSY); 950 } 951 952 @Test 953 @RequiresFlagsEnabled({Flags.FLAG_EPSKC_ENABLED}) 954 public void activateEphemeralKeyMode_notBorderRouter_failsWithFailedPrecondition() 955 throws Exception { 956 setConfigurationAndWait( 957 mController, 958 new ThreadConfiguration.Builder().setBorderRouterEnabled(false).build()); 959 grantPermissions(THREAD_NETWORK_PRIVILEGED); 960 CompletableFuture<Void> future = new CompletableFuture<>(); 961 962 mController.activateEphemeralKeyMode( 963 Duration.ofSeconds(1), mExecutor, newOutcomeReceiver(future)); 964 965 var thrown = 966 assertThrows( 967 ExecutionException.class, 968 () -> future.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS)); 969 var threadException = (ThreadNetworkException) thrown.getCause(); 970 assertThat(threadException.getErrorCode()).isEqualTo(ERROR_FAILED_PRECONDITION); 971 } 972 973 @Test 974 @RequiresFlagsEnabled({Flags.FLAG_EPSKC_ENABLED}) 975 public void deactivateEphemeralKeyMode_withoutPrivilegedPermission_throwsSecurityException() 976 throws Exception { 977 assumeTrue(mDefaultConfig.isBorderRouterEnabled()); 978 dropAllPermissions(); 979 980 assertThrows( 981 SecurityException.class, 982 () -> mController.deactivateEphemeralKeyMode(mExecutor, v -> {})); 983 } 984 985 @Test 986 @RequiresFlagsEnabled({Flags.FLAG_EPSKC_ENABLED}) 987 public void deactivateEphemeralKeyMode_notBorderRouter_failsWithFailedPrecondition() 988 throws Exception { 989 assumeFalse(mDefaultConfig.isBorderRouterEnabled()); 990 grantPermissions(THREAD_NETWORK_PRIVILEGED); 991 CompletableFuture<Void> future = new CompletableFuture<>(); 992 993 mController.deactivateEphemeralKeyMode(mExecutor, newOutcomeReceiver(future)); 994 995 var thrown = 996 assertThrows( 997 ExecutionException.class, 998 () -> future.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS)); 999 var threadException = (ThreadNetworkException) thrown.getCause(); 1000 assertThat(threadException.getErrorCode()).isEqualTo(ERROR_FAILED_PRECONDITION); 1001 } 1002 1003 @Test 1004 @RequiresFlagsEnabled({Flags.FLAG_EPSKC_ENABLED}) 1005 public void subscribeEpskcState_permissionsGranted_returnsCurrentState() throws Exception { 1006 assumeTrue(mDefaultConfig.isBorderRouterEnabled()); 1007 CompletableFuture<Integer> stateFuture = new CompletableFuture<>(); 1008 CompletableFuture<String> ephemeralKeyFuture = new CompletableFuture<>(); 1009 CompletableFuture<Instant> expiryFuture = new CompletableFuture<>(); 1010 StateCallback callback = 1011 new ThreadNetworkController.StateCallback() { 1012 @Override 1013 public void onDeviceRoleChanged(int r) {} 1014 1015 @Override 1016 public void onEphemeralKeyStateChanged( 1017 int state, String ephemeralKey, Instant expiry) { 1018 stateFuture.complete(state); 1019 ephemeralKeyFuture.complete(ephemeralKey); 1020 expiryFuture.complete(expiry); 1021 } 1022 }; 1023 1024 runAsShell( 1025 ACCESS_NETWORK_STATE, 1026 THREAD_NETWORK_PRIVILEGED, 1027 () -> mController.registerStateCallback(mExecutor, callback)); 1028 1029 try { 1030 assertThat(stateFuture.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS)) 1031 .isEqualTo(EPHEMERAL_KEY_DISABLED); 1032 assertThat(ephemeralKeyFuture.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS)).isNull(); 1033 assertThat(expiryFuture.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS)).isNull(); 1034 } finally { 1035 runAsShell(ACCESS_NETWORK_STATE, () -> mController.unregisterStateCallback(callback)); 1036 } 1037 } 1038 1039 @Test 1040 @RequiresFlagsEnabled({Flags.FLAG_EPSKC_ENABLED}) 1041 public void subscribeEpskcState_withoutThreadPriviledgedPermission_returnsNullEphemeralKey() 1042 throws Exception { 1043 assumeTrue(mDefaultConfig.isBorderRouterEnabled()); 1044 CompletableFuture<Integer> stateFuture = new CompletableFuture<>(); 1045 CompletableFuture<String> ephemeralKeyFuture = new CompletableFuture<>(); 1046 CompletableFuture<Instant> expiryFuture = new CompletableFuture<>(); 1047 StateCallback callback = 1048 new ThreadNetworkController.StateCallback() { 1049 @Override 1050 public void onDeviceRoleChanged(int r) {} 1051 1052 @Override 1053 public void onEphemeralKeyStateChanged( 1054 int state, String ephemeralKey, Instant expiry) { 1055 stateFuture.complete(state); 1056 ephemeralKeyFuture.complete(ephemeralKey); 1057 expiryFuture.complete(expiry); 1058 } 1059 }; 1060 joinRandomizedDatasetAndWait(mController); 1061 activateEphemeralKeyModeAndWait(mController); 1062 1063 runAsShell( 1064 ACCESS_NETWORK_STATE, () -> mController.registerStateCallback(mExecutor, callback)); 1065 1066 try { 1067 assertThat(stateFuture.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS)) 1068 .isEqualTo(EPHEMERAL_KEY_ENABLED); 1069 assertThat(ephemeralKeyFuture.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS)).isNull(); 1070 assertThat( 1071 expiryFuture 1072 .get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS) 1073 .isAfter(Instant.now())) 1074 .isTrue(); 1075 } finally { 1076 runAsShell(ACCESS_NETWORK_STATE, () -> mController.unregisterStateCallback(callback)); 1077 } 1078 } 1079 1080 @Test 1081 @RequiresFlagsEnabled({Flags.FLAG_EPSKC_ENABLED}) 1082 public void subscribeEpskcState_ephemralKeyStateChanged_returnsUpdatedState() throws Exception { 1083 assumeTrue(mDefaultConfig.isBorderRouterEnabled()); 1084 EphemeralKeyStateListener listener = new EphemeralKeyStateListener(mController); 1085 joinRandomizedDatasetAndWait(mController); 1086 1087 try { 1088 activateEphemeralKeyModeAndWait(mController); 1089 deactivateEphemeralKeyModeAndWait(mController); 1090 1091 listener.expectThreadEphemeralKeyMode(EPHEMERAL_KEY_DISABLED); 1092 listener.expectThreadEphemeralKeyMode(EPHEMERAL_KEY_ENABLED); 1093 listener.expectThreadEphemeralKeyMode(EPHEMERAL_KEY_DISABLED); 1094 } finally { 1095 listener.unregisterStateCallback(); 1096 } 1097 } 1098 1099 @Test 1100 @RequiresFlagsEnabled({Flags.FLAG_EPSKC_ENABLED}) 1101 public void subscribeEpskcState_epskcEnabled_returnsSameExpiry() throws Exception { 1102 assumeTrue(mDefaultConfig.isBorderRouterEnabled()); 1103 EphemeralKeyStateListener listener1 = new EphemeralKeyStateListener(mController); 1104 Triple<Integer, String, Instant> epskc1; 1105 try { 1106 joinRandomizedDatasetAndWait(mController); 1107 activateEphemeralKeyModeAndWait(mController); 1108 epskc1 = listener1.expectThreadEphemeralKeyMode(EPHEMERAL_KEY_ENABLED); 1109 } finally { 1110 listener1.unregisterStateCallback(); 1111 } 1112 1113 EphemeralKeyStateListener listener2 = new EphemeralKeyStateListener(mController); 1114 try { 1115 Triple<Integer, String, Instant> epskc2 = 1116 listener2.expectThreadEphemeralKeyMode(EPHEMERAL_KEY_ENABLED); 1117 1118 assertThat(epskc2.getSecond()).isEqualTo(epskc1.getSecond()); 1119 // allow time precision loss of a second since the value is passed via IPC 1120 assertThat(epskc2.getThird()).isGreaterThan(epskc1.getThird().minusSeconds(1)); 1121 assertThat(epskc2.getThird()).isLessThan(epskc1.getThird().plusSeconds(1)); 1122 } finally { 1123 listener2.unregisterStateCallback(); 1124 } 1125 } 1126 1127 // TODO (b/322437869): add test case to verify when Thread is in DISABLING state, any commands 1128 // (join/leave/scheduleMigration/setEnabled) fail with ERROR_BUSY. This is not currently tested 1129 // because DISABLING has very short lifecycle, it's not possible to guarantee the command can be 1130 // sent before state changes to DISABLED. 1131 1132 @Test 1133 public void threadNetworkCallback_deviceAttached_threadNetworkIsAvailable() throws Exception { 1134 CompletableFuture<Network> networkFuture = new CompletableFuture<>(); 1135 ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); 1136 NetworkRequest.Builder networkRequestBuilder = 1137 new NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_THREAD); 1138 // Before V, we need to explicitly set `NET_CAPABILITY_LOCAL_NETWORK` capability to request 1139 // a Thread network. 1140 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 1141 networkRequestBuilder.addCapability(NET_CAPABILITY_LOCAL_NETWORK); 1142 } 1143 NetworkRequest networkRequest = networkRequestBuilder.build(); 1144 ConnectivityManager.NetworkCallback networkCallback = 1145 new ConnectivityManager.NetworkCallback() { 1146 @Override 1147 public void onAvailable(Network network) { 1148 networkFuture.complete(network); 1149 } 1150 }; 1151 1152 joinRandomizedDatasetAndWait(mController); 1153 runAsShell( 1154 ACCESS_NETWORK_STATE, 1155 () -> cm.registerNetworkCallback(networkRequest, networkCallback)); 1156 1157 assertThat(isAttached(mController)).isTrue(); 1158 assertThat(networkFuture.get(NETWORK_CALLBACK_TIMEOUT_MILLIS, MILLISECONDS)).isNotNull(); 1159 NetworkCapabilities caps = 1160 runAsShell( 1161 ACCESS_NETWORK_STATE, () -> cm.getNetworkCapabilities(networkFuture.get())); 1162 assertThat(caps).isNotNull(); 1163 assertThat(caps.hasTransport(NetworkCapabilities.TRANSPORT_THREAD)).isTrue(); 1164 assertThat(caps.getCapabilities()) 1165 .asList() 1166 .containsAtLeast( 1167 NET_CAPABILITY_LOCAL_NETWORK, 1168 NET_CAPABILITY_NOT_METERED, 1169 NET_CAPABILITY_NOT_RESTRICTED, 1170 NET_CAPABILITY_NOT_VCN_MANAGED, 1171 NET_CAPABILITY_NOT_VPN, 1172 NET_CAPABILITY_TRUSTED); 1173 } 1174 1175 @Test 1176 public void setConfiguration_null_throwsNullPointerException() throws Exception { 1177 CompletableFuture<Void> setConfigFuture = new CompletableFuture<>(); 1178 assertThrows( 1179 NullPointerException.class, 1180 () -> 1181 mController.setConfiguration( 1182 null, mExecutor, newOutcomeReceiver(setConfigFuture))); 1183 } 1184 1185 @Test 1186 public void setConfiguration_noPermissions_throwsSecurityException() throws Exception { 1187 ThreadConfiguration configuration = 1188 new ThreadConfiguration.Builder().setNat64Enabled(true).build(); 1189 CompletableFuture<Void> setConfigFuture = new CompletableFuture<>(); 1190 assertThrows( 1191 SecurityException.class, 1192 () -> { 1193 mController.setConfiguration( 1194 configuration, mExecutor, newOutcomeReceiver(setConfigFuture)); 1195 }); 1196 } 1197 1198 @Test 1199 public void registerConfigurationCallback_permissionsGranted_returnsCurrentStatus() 1200 throws Exception { 1201 CompletableFuture<ThreadConfiguration> getConfigFuture = new CompletableFuture<>(); 1202 Consumer<ThreadConfiguration> callback = getConfigFuture::complete; 1203 1204 runAsShell( 1205 THREAD_NETWORK_PRIVILEGED, 1206 () -> registerConfigurationCallback(mController, mExecutor, callback)); 1207 assertThat(getConfigFuture.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS)) 1208 .isEqualTo(mDefaultConfig); 1209 } 1210 1211 @Test 1212 public void registerConfigurationCallback_noPermissions_throwsSecurityException() 1213 throws Exception { 1214 dropAllPermissions(); 1215 1216 assertThrows( 1217 SecurityException.class, 1218 () -> registerConfigurationCallback(mController, mExecutor, config -> {})); 1219 } 1220 1221 @Test 1222 public void registerConfigurationCallback_returnsUpdatedConfigurations() throws Exception { 1223 CompletableFuture<Void> setFuture1 = new CompletableFuture<>(); 1224 CompletableFuture<Void> setFuture2 = new CompletableFuture<>(); 1225 ConfigurationListener listener = new ConfigurationListener(mController); 1226 ThreadConfiguration config1 = 1227 new ThreadConfiguration.Builder() 1228 .setBorderRouterEnabled(true) 1229 .setNat64Enabled(true) 1230 .build(); 1231 ThreadConfiguration config2 = 1232 new ThreadConfiguration.Builder() 1233 .setBorderRouterEnabled(false) 1234 .setNat64Enabled(false) 1235 .build(); 1236 1237 try { 1238 runAsShell( 1239 THREAD_NETWORK_PRIVILEGED, 1240 () -> 1241 mController.setConfiguration( 1242 config1, mExecutor, newOutcomeReceiver(setFuture1))); 1243 runAsShell( 1244 THREAD_NETWORK_PRIVILEGED, 1245 () -> 1246 mController.setConfiguration( 1247 config2, mExecutor, newOutcomeReceiver(setFuture2))); 1248 setFuture1.get(ENABLED_TIMEOUT_MILLIS, MILLISECONDS); 1249 setFuture2.get(ENABLED_TIMEOUT_MILLIS, MILLISECONDS); 1250 1251 listener.expectConfiguration(mDefaultConfig); 1252 listener.expectConfiguration(config1); 1253 listener.expectConfiguration(config2); 1254 listener.expectNoMoreConfiguration(); 1255 } finally { 1256 listener.unregisterConfigurationCallback(); 1257 } 1258 } 1259 1260 @Test 1261 public void registerConfigurationCallback_alreadyRegistered_throwsIllegalArgumentException() 1262 throws Exception { 1263 grantPermissions(THREAD_NETWORK_PRIVILEGED); 1264 1265 Consumer<ThreadConfiguration> callback = config -> {}; 1266 registerConfigurationCallback(mController, mExecutor, callback); 1267 1268 assertThrows( 1269 IllegalArgumentException.class, 1270 () -> registerConfigurationCallback(mController, mExecutor, callback)); 1271 } 1272 1273 @Test 1274 public void unregisterConfigurationCallback_noPermissions_throwsSecurityException() 1275 throws Exception { 1276 Consumer<ThreadConfiguration> callback = config -> {}; 1277 runAsShell( 1278 THREAD_NETWORK_PRIVILEGED, 1279 () -> registerConfigurationCallback(mController, mExecutor, callback)); 1280 1281 assertThrows( 1282 SecurityException.class, 1283 () -> mController.unregisterConfigurationCallback(callback)); 1284 } 1285 1286 @Test 1287 public void unregisterConfigurationCallback_callbackRegistered_success() throws Exception { 1288 Consumer<ThreadConfiguration> callback = config -> {}; 1289 runAsShell( 1290 THREAD_NETWORK_PRIVILEGED, 1291 () -> { 1292 registerConfigurationCallback(mController, mExecutor, callback); 1293 mController.unregisterConfigurationCallback(callback); 1294 }); 1295 } 1296 1297 @Test 1298 public void 1299 unregisterConfigurationCallback_callbackNotRegistered_throwsIllegalArgumentException() 1300 throws Exception { 1301 Consumer<ThreadConfiguration> callback = config -> {}; 1302 assertThrows( 1303 IllegalArgumentException.class, 1304 () -> mController.unregisterConfigurationCallback(callback)); 1305 } 1306 1307 @Test 1308 public void unregisterConfigurationCallback_alreadyUnregistered_throwsIllegalArgumentException() 1309 throws Exception { 1310 grantPermissions(THREAD_NETWORK_PRIVILEGED); 1311 1312 Consumer<ThreadConfiguration> callback = config -> {}; 1313 registerConfigurationCallback(mController, mExecutor, callback); 1314 mController.unregisterConfigurationCallback(callback); 1315 1316 assertThrows( 1317 IllegalArgumentException.class, 1318 () -> mController.unregisterConfigurationCallback(callback)); 1319 } 1320 1321 private void grantPermissions(String... permissions) { 1322 for (String permission : permissions) { 1323 mGrantedPermissions.add(permission); 1324 } 1325 String[] allPermissions = new String[mGrantedPermissions.size()]; 1326 mGrantedPermissions.toArray(allPermissions); 1327 getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(allPermissions); 1328 } 1329 1330 @Test 1331 @Ignore("b/333649897, b/332195449: The 3 meshcop tests are flaky in different environments") 1332 public void meshcopService_threadEnabledButNotJoined_discoveredButNoNetwork() throws Exception { 1333 setUpTestNetwork(); 1334 1335 setEnabledAndWait(mController, true); 1336 leaveAndWait(mController); 1337 1338 NsdServiceInfo serviceInfo = 1339 expectServiceResolved( 1340 MESHCOP_SERVICE_TYPE, 1341 SERVICE_DISCOVERY_TIMEOUT_MILLIS, 1342 s -> s.getAttributes().get("at") == null); 1343 1344 Map<String, byte[]> txtMap = serviceInfo.getAttributes(); 1345 1346 assertThat(txtMap.get("rv")).isNotNull(); 1347 assertThat(txtMap.get("tv")).isNotNull(); 1348 // Border Agent State Bitmap is 32 bits 1349 assertThat(txtMap.get("sb").length).isEqualTo(4); 1350 // The 12th bit (4th bit of the second byte) for ePSKc support should be set to 1. 1351 assertThat(txtMap.get("sb")[2] & 8).isEqualTo(8); 1352 } 1353 1354 @Test 1355 @Ignore("b/333649897: Enable this when it's not flaky at all") 1356 public void meshcopService_joinedNetwork_discoveredHasNetwork() throws Exception { 1357 setUpTestNetwork(); 1358 1359 String networkName = "TestNet" + new Random().nextInt(10_000); 1360 joinRandomizedDatasetAndWait(mController, networkName); 1361 1362 Predicate<NsdServiceInfo> predicate = 1363 serviceInfo -> 1364 serviceInfo.getAttributes().get("at") != null 1365 && Arrays.equals( 1366 serviceInfo.getAttributes().get("nn"), 1367 networkName.getBytes(StandardCharsets.UTF_8)); 1368 1369 NsdServiceInfo resolvedService = 1370 expectServiceResolved( 1371 MESHCOP_SERVICE_TYPE, SERVICE_DISCOVERY_TIMEOUT_MILLIS, predicate); 1372 1373 Map<String, byte[]> txtMap = resolvedService.getAttributes(); 1374 assertThat(txtMap.get("rv")).isNotNull(); 1375 assertThat(txtMap.get("tv")).isNotNull(); 1376 // Border Agent State Bitmap is 32 bits 1377 assertThat(txtMap.get("sb").length).isEqualTo(4); 1378 // The 12th bit (4th bit of the second byte) for ePSKc support should be set to 1. 1379 assertThat(txtMap.get("sb")[2] & 8).isEqualTo(8); 1380 assertThat(txtMap.get("id").length).isEqualTo(16); 1381 } 1382 1383 @Test 1384 @Ignore("b/333649897, b/332195449: The 3 meshcop tests are flaky in different environments") 1385 public void meshcopService_threadDisabled_notDiscovered() throws Exception { 1386 setUpTestNetwork(); 1387 CompletableFuture<NsdServiceInfo> serviceLostFuture = new CompletableFuture<>(); 1388 NsdManager.DiscoveryListener listener = 1389 discoverForServiceLost(MESHCOP_SERVICE_TYPE, serviceLostFuture); 1390 1391 setEnabledAndWait(mController, false); 1392 1393 try { 1394 serviceLostFuture.get(SERVICE_LOST_TIMEOUT_MILLIS, MILLISECONDS); 1395 } catch (InterruptedException | ExecutionException | TimeoutException ignored) { 1396 // It's fine if the service lost event didn't show up. The service may not ever be 1397 // advertised. 1398 } finally { 1399 mNsdManager.stopServiceDiscovery(listener); 1400 } 1401 assertThrows( 1402 TimeoutException.class, 1403 () -> discoverService(MESHCOP_SERVICE_TYPE, SERVICE_LOST_TIMEOUT_MILLIS)); 1404 } 1405 1406 private static void dropAllPermissions() { 1407 getInstrumentation().getUiAutomation().dropShellPermissionIdentity(); 1408 } 1409 1410 private static ActiveOperationalDataset newRandomizedDataset( 1411 String networkName, ThreadNetworkController controller) throws Exception { 1412 CompletableFuture<ActiveOperationalDataset> future = new CompletableFuture<>(); 1413 controller.createRandomizedDataset(networkName, directExecutor(), future::complete); 1414 return future.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS); 1415 } 1416 1417 private static boolean isAttached(ThreadNetworkController controller) throws Exception { 1418 return ThreadNetworkController.isAttached(getDeviceRole(controller)); 1419 } 1420 1421 private static int getDeviceRole(ThreadNetworkController controller) throws Exception { 1422 CompletableFuture<Integer> future = new CompletableFuture<>(); 1423 StateCallback callback = future::complete; 1424 runAsShell( 1425 ACCESS_NETWORK_STATE, 1426 () -> controller.registerStateCallback(directExecutor(), callback)); 1427 try { 1428 return future.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS); 1429 } finally { 1430 runAsShell(ACCESS_NETWORK_STATE, () -> controller.unregisterStateCallback(callback)); 1431 } 1432 } 1433 1434 private static int waitForAttachedState(ThreadNetworkController controller) throws Exception { 1435 List<Integer> attachedRoles = new ArrayList<>(); 1436 attachedRoles.add(DEVICE_ROLE_CHILD); 1437 attachedRoles.add(DEVICE_ROLE_ROUTER); 1438 attachedRoles.add(DEVICE_ROLE_LEADER); 1439 return waitForStateAnyOf(controller, attachedRoles); 1440 } 1441 1442 private static int waitForStateAnyOf( 1443 ThreadNetworkController controller, List<Integer> deviceRoles) throws Exception { 1444 CompletableFuture<Integer> future = new CompletableFuture<>(); 1445 StateCallback callback = 1446 newRole -> { 1447 if (deviceRoles.contains(newRole)) { 1448 future.complete(newRole); 1449 } 1450 }; 1451 controller.registerStateCallback(directExecutor(), callback); 1452 int role = future.get(JOIN_TIMEOUT_MILLIS, MILLISECONDS); 1453 controller.unregisterStateCallback(callback); 1454 return role; 1455 } 1456 1457 private static void waitForEnabledState(ThreadNetworkController controller, int state) 1458 throws Exception { 1459 CompletableFuture<Integer> future = new CompletableFuture<>(); 1460 StateCallback callback = 1461 new ThreadNetworkController.StateCallback() { 1462 @Override 1463 public void onDeviceRoleChanged(int r) {} 1464 1465 @Override 1466 public void onThreadEnableStateChanged(int enabled) { 1467 if (enabled == state) { 1468 future.complete(enabled); 1469 } 1470 } 1471 }; 1472 runAsShell( 1473 ACCESS_NETWORK_STATE, 1474 () -> controller.registerStateCallback(directExecutor(), callback)); 1475 future.get(ENABLED_TIMEOUT_MILLIS, MILLISECONDS); 1476 runAsShell(ACCESS_NETWORK_STATE, () -> controller.unregisterStateCallback(callback)); 1477 } 1478 1479 private void leave( 1480 ThreadNetworkController controller, 1481 OutcomeReceiver<Void, ThreadNetworkException> receiver) { 1482 runAsShell(THREAD_NETWORK_PRIVILEGED, () -> controller.leave(mExecutor, receiver)); 1483 } 1484 1485 private void leaveAndWait(ThreadNetworkController controller) throws Exception { 1486 CompletableFuture<Void> future = new CompletableFuture<>(); 1487 leave(controller, future::complete); 1488 future.get(LEAVE_TIMEOUT_MILLIS, MILLISECONDS); 1489 } 1490 1491 private void scheduleMigration( 1492 ThreadNetworkController controller, 1493 PendingOperationalDataset pendingDataset, 1494 OutcomeReceiver<Void, ThreadNetworkException> receiver) { 1495 runAsShell( 1496 THREAD_NETWORK_PRIVILEGED, 1497 () -> controller.scheduleMigration(pendingDataset, mExecutor, receiver)); 1498 } 1499 1500 private class EnabledStateListener { 1501 private ArrayTrackRecord<Integer> mEnabledStates = new ArrayTrackRecord<>(); 1502 private final ArrayTrackRecord<Integer>.ReadHead mReadHead = mEnabledStates.newReadHead(); 1503 ThreadNetworkController mController; 1504 StateCallback mCallback = 1505 new ThreadNetworkController.StateCallback() { 1506 @Override 1507 public void onDeviceRoleChanged(int r) {} 1508 1509 @Override 1510 public void onThreadEnableStateChanged(int enabled) { 1511 mEnabledStates.add(enabled); 1512 } 1513 }; 1514 1515 EnabledStateListener(ThreadNetworkController controller) { 1516 this.mController = controller; 1517 runAsShell( 1518 ACCESS_NETWORK_STATE, 1519 () -> controller.registerStateCallback(mExecutor, mCallback)); 1520 } 1521 1522 public void expectThreadEnabledState(int enabled) { 1523 assertThat(mReadHead.poll(ENABLED_TIMEOUT_MILLIS, e -> (e == enabled))).isNotNull(); 1524 } 1525 1526 public void expectCallbackNotCalled() { 1527 assertThat(mReadHead.poll(CALLBACK_TIMEOUT_MILLIS, e -> true)).isNull(); 1528 } 1529 1530 public void unregisterStateCallback() { 1531 runAsShell(ACCESS_NETWORK_STATE, () -> mController.unregisterStateCallback(mCallback)); 1532 } 1533 } 1534 1535 private class ConfigurationListener { 1536 private ArrayTrackRecord<ThreadConfiguration> mConfigurations = new ArrayTrackRecord<>(); 1537 private final ArrayTrackRecord<ThreadConfiguration>.ReadHead mReadHead = 1538 mConfigurations.newReadHead(); 1539 ThreadNetworkController mController; 1540 Consumer<ThreadConfiguration> mCallback = (config) -> mConfigurations.add(config); 1541 1542 ConfigurationListener(ThreadNetworkController controller) { 1543 this.mController = controller; 1544 runAsShell( 1545 THREAD_NETWORK_PRIVILEGED, 1546 () -> controller.registerConfigurationCallback(mExecutor, mCallback)); 1547 } 1548 1549 public void expectConfiguration(ThreadConfiguration config) { 1550 assertThat(mReadHead.poll(CALLBACK_TIMEOUT_MILLIS, c -> c.equals(config))).isNotNull(); 1551 } 1552 1553 public void expectNoMoreConfiguration() { 1554 assertThat(mReadHead.poll(CALLBACK_TIMEOUT_MILLIS, c -> true)).isNull(); 1555 } 1556 1557 public void unregisterConfigurationCallback() { 1558 runAsShell( 1559 THREAD_NETWORK_PRIVILEGED, 1560 () -> mController.unregisterConfigurationCallback(mCallback)); 1561 } 1562 } 1563 1564 private int booleanToEnabledState(boolean enabled) { 1565 return enabled ? STATE_ENABLED : STATE_DISABLED; 1566 } 1567 1568 private void setEnabledAndWait(ThreadNetworkController controller, boolean enabled) 1569 throws Exception { 1570 CompletableFuture<Void> setFuture = new CompletableFuture<>(); 1571 runAsShell( 1572 THREAD_NETWORK_PRIVILEGED, 1573 () -> controller.setEnabled(enabled, mExecutor, newOutcomeReceiver(setFuture))); 1574 setFuture.get(ENABLED_TIMEOUT_MILLIS, MILLISECONDS); 1575 waitForEnabledState(controller, booleanToEnabledState(enabled)); 1576 } 1577 1578 private void setConfigurationAndWait( 1579 ThreadNetworkController controller, ThreadConfiguration configuration) 1580 throws Exception { 1581 CompletableFuture<Void> setFuture = new CompletableFuture<>(); 1582 runAsShell( 1583 THREAD_NETWORK_PRIVILEGED, 1584 () -> 1585 controller.setConfiguration( 1586 configuration, mExecutor, newOutcomeReceiver(setFuture))); 1587 setFuture.get(SET_CONFIGURATION_TIMEOUT_MILLIS, MILLISECONDS); 1588 } 1589 1590 private void deactivateEphemeralKeyModeAndWait(ThreadNetworkController controller) 1591 throws Exception { 1592 CompletableFuture<Void> clearFuture = new CompletableFuture<>(); 1593 runAsShell( 1594 THREAD_NETWORK_PRIVILEGED, 1595 () -> 1596 controller.deactivateEphemeralKeyMode( 1597 mExecutor, newOutcomeReceiver(clearFuture))); 1598 clearFuture.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS); 1599 } 1600 1601 private void activateEphemeralKeyModeAndWait(ThreadNetworkController controller) 1602 throws Exception { 1603 CompletableFuture<Void> startFuture = new CompletableFuture<>(); 1604 runAsShell( 1605 THREAD_NETWORK_PRIVILEGED, 1606 () -> 1607 controller.activateEphemeralKeyMode( 1608 EPHEMERAL_KEY_LIFETIME, 1609 mExecutor, 1610 newOutcomeReceiver(startFuture))); 1611 startFuture.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS); 1612 } 1613 1614 private class EphemeralKeyStateListener { 1615 private ArrayTrackRecord<Triple<Integer, String, Instant>> mEphemeralKeyStates = 1616 new ArrayTrackRecord<>(); 1617 private final ArrayTrackRecord<Triple<Integer, String, Instant>>.ReadHead mReadHead = 1618 mEphemeralKeyStates.newReadHead(); 1619 ThreadNetworkController mController; 1620 StateCallback mCallback = 1621 new ThreadNetworkController.StateCallback() { 1622 @Override 1623 public void onDeviceRoleChanged(int r) {} 1624 1625 @Override 1626 public void onEphemeralKeyStateChanged( 1627 int state, String ephemeralKey, Instant expiry) { 1628 mEphemeralKeyStates.add(new Triple<>(state, ephemeralKey, expiry)); 1629 } 1630 }; 1631 1632 EphemeralKeyStateListener(ThreadNetworkController controller) { 1633 this.mController = controller; 1634 runAsShell( 1635 ACCESS_NETWORK_STATE, 1636 THREAD_NETWORK_PRIVILEGED, 1637 () -> controller.registerStateCallback(mExecutor, mCallback)); 1638 } 1639 1640 // Expect that EphemeralKey has the expected state, and return a Triple of <state, 1641 // passcode, expiry>. 1642 public Triple<Integer, String, Instant> expectThreadEphemeralKeyMode(int state) { 1643 Triple<Integer, String, Instant> epskc = 1644 mReadHead.poll( 1645 ENABLED_TIMEOUT_MILLIS, e -> Objects.equals(e.getFirst(), state)); 1646 assertThat(epskc).isNotNull(); 1647 return epskc; 1648 } 1649 1650 public void unregisterStateCallback() { 1651 runAsShell(ACCESS_NETWORK_STATE, () -> mController.unregisterStateCallback(mCallback)); 1652 } 1653 } 1654 1655 private CompletableFuture joinRandomizedDataset( 1656 ThreadNetworkController controller, String networkName) throws Exception { 1657 ActiveOperationalDataset activeDataset = newRandomizedDataset(networkName, controller); 1658 CompletableFuture<Void> joinFuture = new CompletableFuture<>(); 1659 runAsShell( 1660 THREAD_NETWORK_PRIVILEGED, 1661 () -> controller.join(activeDataset, mExecutor, newOutcomeReceiver(joinFuture))); 1662 return joinFuture; 1663 } 1664 1665 private void joinRandomizedDatasetAndWait(ThreadNetworkController controller) throws Exception { 1666 joinRandomizedDatasetAndWait(controller, "TestNet"); 1667 } 1668 1669 private void joinRandomizedDatasetAndWait( 1670 ThreadNetworkController controller, String networkName) throws Exception { 1671 CompletableFuture<Void> joinFuture = joinRandomizedDataset(controller, networkName); 1672 joinFuture.get(JOIN_TIMEOUT_MILLIS, MILLISECONDS); 1673 assertThat(isAttached(controller)).isTrue(); 1674 } 1675 1676 private static ActiveOperationalDataset getActiveOperationalDataset( 1677 ThreadNetworkController controller) throws Exception { 1678 CompletableFuture<ActiveOperationalDataset> future = new CompletableFuture<>(); 1679 OperationalDatasetCallback callback = future::complete; 1680 runAsShell( 1681 ACCESS_NETWORK_STATE, 1682 THREAD_NETWORK_PRIVILEGED, 1683 () -> controller.registerOperationalDatasetCallback(directExecutor(), callback)); 1684 try { 1685 return future.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS); 1686 } finally { 1687 runAsShell( 1688 ACCESS_NETWORK_STATE, 1689 THREAD_NETWORK_PRIVILEGED, 1690 () -> controller.unregisterOperationalDatasetCallback(callback)); 1691 } 1692 } 1693 1694 private static PendingOperationalDataset getPendingOperationalDataset( 1695 ThreadNetworkController controller) throws Exception { 1696 CompletableFuture<ActiveOperationalDataset> activeFuture = new CompletableFuture<>(); 1697 CompletableFuture<PendingOperationalDataset> pendingFuture = new CompletableFuture<>(); 1698 controller.registerOperationalDatasetCallback( 1699 directExecutor(), newDatasetCallback(activeFuture, pendingFuture)); 1700 return pendingFuture.get(CALLBACK_TIMEOUT_MILLIS, MILLISECONDS); 1701 } 1702 1703 private static OperationalDatasetCallback newDatasetCallback( 1704 CompletableFuture<ActiveOperationalDataset> activeFuture, 1705 CompletableFuture<PendingOperationalDataset> pendingFuture) { 1706 return new OperationalDatasetCallback() { 1707 @Override 1708 public void onActiveOperationalDatasetChanged( 1709 ActiveOperationalDataset activeOpDataset) { 1710 activeFuture.complete(activeOpDataset); 1711 } 1712 1713 @Override 1714 public void onPendingOperationalDatasetChanged( 1715 PendingOperationalDataset pendingOpDataset) { 1716 pendingFuture.complete(pendingOpDataset); 1717 } 1718 }; 1719 } 1720 1721 private void registerConfigurationCallback( 1722 ThreadNetworkController controller, 1723 Executor executor, 1724 Consumer<ThreadConfiguration> callback) { 1725 controller.registerConfigurationCallback(executor, callback); 1726 mConfigurationCallbacksToCleanUp.add(callback); 1727 } 1728 1729 private static void assertDoesNotThrow(ThrowingRunnable runnable) { 1730 try { 1731 runnable.run(); 1732 } catch (Throwable e) { 1733 fail("Should not have thrown " + e); 1734 } 1735 } 1736 1737 // Return the first discovered service instance. 1738 private NsdServiceInfo discoverService(String serviceType) throws Exception { 1739 return discoverService(serviceType, SERVICE_DISCOVERY_TIMEOUT_MILLIS); 1740 } 1741 1742 // Return the first discovered service instance. 1743 private NsdServiceInfo discoverService(String serviceType, int timeoutMilliseconds) 1744 throws Exception { 1745 CompletableFuture<NsdServiceInfo> serviceInfoFuture = new CompletableFuture<>(); 1746 NsdManager.DiscoveryListener listener = 1747 new DefaultDiscoveryListener() { 1748 @Override 1749 public void onServiceFound(NsdServiceInfo serviceInfo) { 1750 serviceInfoFuture.complete(serviceInfo); 1751 } 1752 }; 1753 mNsdManager.discoverServices( 1754 serviceType, 1755 NsdManager.PROTOCOL_DNS_SD, 1756 mTestNetworkTracker.getNetwork(), 1757 mExecutor, 1758 listener); 1759 try { 1760 serviceInfoFuture.get(timeoutMilliseconds, MILLISECONDS); 1761 } finally { 1762 mNsdManager.stopServiceDiscovery(listener); 1763 } 1764 1765 return serviceInfoFuture.get(); 1766 } 1767 1768 private NsdManager.DiscoveryListener discoverForServiceLost( 1769 String serviceType, CompletableFuture<NsdServiceInfo> serviceInfoFuture) { 1770 NsdManager.DiscoveryListener listener = 1771 new DefaultDiscoveryListener() { 1772 @Override 1773 public void onServiceLost(NsdServiceInfo serviceInfo) { 1774 serviceInfoFuture.complete(serviceInfo); 1775 } 1776 }; 1777 mNsdManager.discoverServices( 1778 serviceType, 1779 NsdManager.PROTOCOL_DNS_SD, 1780 mTestNetworkTracker.getNetwork(), 1781 mExecutor, 1782 listener); 1783 return listener; 1784 } 1785 1786 private NsdServiceInfo expectServiceResolved( 1787 String serviceType, int timeoutMilliseconds, Predicate<NsdServiceInfo> predicate) 1788 throws Exception { 1789 NsdServiceInfo discoveredServiceInfo = discoverService(serviceType); 1790 CompletableFuture<NsdServiceInfo> future = new CompletableFuture<>(); 1791 NsdManager.ServiceInfoCallback callback = 1792 new DefaultServiceInfoCallback() { 1793 @Override 1794 public void onServiceUpdated(@NonNull NsdServiceInfo serviceInfo) { 1795 if (predicate.test(serviceInfo)) { 1796 future.complete(serviceInfo); 1797 } 1798 } 1799 }; 1800 mNsdManager.registerServiceInfoCallback(discoveredServiceInfo, mExecutor, callback); 1801 try { 1802 return future.get(timeoutMilliseconds, MILLISECONDS); 1803 } finally { 1804 mNsdManager.unregisterServiceInfoCallback(callback); 1805 } 1806 } 1807 1808 private void setUpTestNetwork() { 1809 assertThat(mTestNetworkTracker).isNull(); 1810 mTestNetworkTracker = new TapTestNetworkTracker(mContext, mHandlerThread.getLooper()); 1811 } 1812 1813 private void tearDownTestNetwork() throws InterruptedException { 1814 if (mTestNetworkTracker != null) { 1815 mTestNetworkTracker.tearDown(); 1816 } 1817 mHandlerThread.quitSafely(); 1818 mHandlerThread.join(); 1819 } 1820 1821 private static class DefaultDiscoveryListener implements NsdManager.DiscoveryListener { 1822 @Override 1823 public void onStartDiscoveryFailed(String serviceType, int errorCode) {} 1824 1825 @Override 1826 public void onStopDiscoveryFailed(String serviceType, int errorCode) {} 1827 1828 @Override 1829 public void onDiscoveryStarted(String serviceType) {} 1830 1831 @Override 1832 public void onDiscoveryStopped(String serviceType) {} 1833 1834 @Override 1835 public void onServiceFound(NsdServiceInfo serviceInfo) {} 1836 1837 @Override 1838 public void onServiceLost(NsdServiceInfo serviceInfo) {} 1839 } 1840 1841 private static class DefaultServiceInfoCallback implements NsdManager.ServiceInfoCallback { 1842 @Override 1843 public void onServiceInfoCallbackRegistrationFailed(int errorCode) {} 1844 1845 @Override 1846 public void onServiceUpdated(@NonNull NsdServiceInfo serviceInfo) {} 1847 1848 @Override 1849 public void onServiceLost() {} 1850 1851 @Override 1852 public void onServiceInfoCallbackUnregistered() {} 1853 } 1854 1855 @Test 1856 @RequiresFlagsEnabled({Flags.FLAG_CHANNEL_MAX_POWERS_ENABLED}) 1857 public void setChannelMaxPowers_withPrivilegedPermission_success() throws Exception { 1858 CompletableFuture<Void> powerFuture = new CompletableFuture<>(); 1859 1860 runAsShell( 1861 THREAD_NETWORK_PRIVILEGED, 1862 () -> 1863 mController.setChannelMaxPowers( 1864 CHANNEL_MAX_POWERS, mExecutor, newOutcomeReceiver(powerFuture))); 1865 1866 try { 1867 assertThat(powerFuture.get()).isNull(); 1868 } catch (ExecutionException exception) { 1869 ThreadNetworkException thrown = (ThreadNetworkException) exception.getCause(); 1870 assertThat(thrown.getErrorCode()).isEqualTo(ERROR_UNSUPPORTED_FEATURE); 1871 } 1872 } 1873 1874 @Test 1875 @RequiresFlagsEnabled({Flags.FLAG_CHANNEL_MAX_POWERS_ENABLED}) 1876 public void setChannelMaxPowers_withoutPrivilegedPermission_throwsSecurityException() 1877 throws Exception { 1878 dropAllPermissions(); 1879 1880 assertThrows( 1881 SecurityException.class, 1882 () -> mController.setChannelMaxPowers(CHANNEL_MAX_POWERS, mExecutor, v -> {})); 1883 } 1884 1885 @Test 1886 @RequiresFlagsEnabled({Flags.FLAG_CHANNEL_MAX_POWERS_ENABLED}) 1887 public void setChannelMaxPowers_invalidChannel_throwsIllegalArgumentException() { 1888 final SparseIntArray INVALID_CHANNEL_ARRAY = 1889 new SparseIntArray() { 1890 { 1891 put(INVALID_CHANNEL, VALID_POWER); 1892 } 1893 }; 1894 1895 assertThrows( 1896 IllegalArgumentException.class, 1897 () -> mController.setChannelMaxPowers(new SparseIntArray(), mExecutor, v -> {})); 1898 assertThrows( 1899 IllegalArgumentException.class, 1900 () -> mController.setChannelMaxPowers(INVALID_CHANNEL_ARRAY, mExecutor, v -> {})); 1901 } 1902 } 1903