1 /* 2 * Copyright (C) 2020 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.cts.util; 18 19 import static android.Manifest.permission.ACCESS_NETWORK_STATE; 20 import static android.Manifest.permission.ACCESS_WIFI_STATE; 21 import static android.Manifest.permission.NETWORK_SETTINGS; 22 import static android.Manifest.permission.TETHER_PRIVILEGED; 23 import static android.net.TetheringManager.TETHERING_WIFI; 24 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; 25 import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_REQUEST; 26 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED; 27 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED; 28 import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED; 29 30 import static com.android.testutils.TestPermissionUtil.runAsShell; 31 32 import static org.junit.Assert.assertEquals; 33 import static org.junit.Assert.assertNotNull; 34 import static org.junit.Assert.assertTrue; 35 import static org.junit.Assert.fail; 36 import static org.junit.Assume.assumeTrue; 37 38 import android.content.BroadcastReceiver; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.IntentFilter; 42 import android.content.pm.PackageManager; 43 import android.net.Network; 44 import android.net.TetheredClient; 45 import android.net.TetheringInterface; 46 import android.net.TetheringManager; 47 import android.net.TetheringManager.TetheringEventCallback; 48 import android.net.TetheringManager.TetheringInterfaceRegexps; 49 import android.net.TetheringManager.TetheringRequest; 50 import android.net.wifi.SoftApConfiguration; 51 import android.net.wifi.WifiClient; 52 import android.net.wifi.WifiManager; 53 import android.net.wifi.WifiManager.SoftApCallback; 54 import android.os.ConditionVariable; 55 56 import androidx.annotation.NonNull; 57 import androidx.annotation.Nullable; 58 59 import com.android.compatibility.common.util.SystemUtil; 60 import com.android.net.module.util.ArrayTrackRecord; 61 62 import java.util.Collection; 63 import java.util.List; 64 import java.util.Set; 65 66 public final class CtsTetheringUtils { 67 private TetheringManager mTm; 68 private WifiManager mWm; 69 private Context mContext; 70 71 private static final int DEFAULT_TIMEOUT_MS = 60_000; 72 CtsTetheringUtils(Context ctx)73 public CtsTetheringUtils(Context ctx) { 74 mContext = ctx; 75 mTm = mContext.getSystemService(TetheringManager.class); 76 mWm = mContext.getSystemService(WifiManager.class); 77 } 78 79 public static class StartTetheringCallback implements TetheringManager.StartTetheringCallback { 80 private static int TIMEOUT_MS = 30_000; 81 public static class CallbackValue { 82 public final int error; 83 CallbackValue(final int e)84 private CallbackValue(final int e) { 85 error = e; 86 } 87 88 public static class OnTetheringStarted extends CallbackValue { OnTetheringStarted()89 OnTetheringStarted() { super(TETHER_ERROR_NO_ERROR); } 90 } 91 92 public static class OnTetheringFailed extends CallbackValue { OnTetheringFailed(final int error)93 OnTetheringFailed(final int error) { super(error); } 94 } 95 96 @Override toString()97 public String toString() { 98 return String.format("%s(%d)", getClass().getSimpleName(), error); 99 } 100 } 101 102 private final ArrayTrackRecord<CallbackValue>.ReadHead mHistory = 103 new ArrayTrackRecord<CallbackValue>().newReadHead(); 104 105 @Override onTetheringStarted()106 public void onTetheringStarted() { 107 mHistory.add(new CallbackValue.OnTetheringStarted()); 108 } 109 110 @Override onTetheringFailed(final int error)111 public void onTetheringFailed(final int error) { 112 mHistory.add(new CallbackValue.OnTetheringFailed(error)); 113 } 114 verifyTetheringStarted()115 public void verifyTetheringStarted() { 116 final CallbackValue cv = mHistory.poll(TIMEOUT_MS, c -> true); 117 assertNotNull("No onTetheringStarted after " + TIMEOUT_MS + " ms", cv); 118 assertTrue("Fail start tethering:" + cv, 119 cv instanceof CallbackValue.OnTetheringStarted); 120 } 121 122 /** 123 * Verify that starting tethering failed with the specified error code. 124 */ expectTetheringFailed(final int expected)125 public void expectTetheringFailed(final int expected) { 126 final CallbackValue cv = mHistory.poll(TIMEOUT_MS, c -> true); 127 assertNotNull("No onTetheringFailed after " + TIMEOUT_MS + " ms", cv); 128 assertTrue("Expect fail with error code " + expected + ", but received: " + cv, 129 (cv instanceof CallbackValue.OnTetheringFailed) && (cv.error == expected)); 130 } 131 } 132 133 public static class StopTetheringCallback implements TetheringManager.StopTetheringCallback { 134 private static final int TIMEOUT_MS = 30_000; 135 public static class CallbackValue { 136 public final int error; 137 CallbackValue(final int e)138 private CallbackValue(final int e) { 139 error = e; 140 } 141 142 public static class OnStopTetheringSucceeded extends CallbackValue { OnStopTetheringSucceeded()143 OnStopTetheringSucceeded() { 144 super(TETHER_ERROR_NO_ERROR); 145 } 146 } 147 148 public static class OnStopTetheringFailed extends CallbackValue { OnStopTetheringFailed(final int error)149 OnStopTetheringFailed(final int error) { 150 super(error); 151 } 152 } 153 154 @Override toString()155 public String toString() { 156 return String.format("%s(%d)", getClass().getSimpleName(), error); 157 } 158 } 159 160 private final ArrayTrackRecord<CallbackValue>.ReadHead mHistory = 161 new ArrayTrackRecord<CallbackValue>().newReadHead(); 162 163 @Override onStopTetheringSucceeded()164 public void onStopTetheringSucceeded() { 165 mHistory.add(new CallbackValue.OnStopTetheringSucceeded()); 166 // Call the parent method so that the coverage linter sees it: http://b/385014495 167 TetheringManager.StopTetheringCallback.super.onStopTetheringSucceeded(); 168 } 169 170 @Override onStopTetheringFailed(final int error)171 public void onStopTetheringFailed(final int error) { 172 mHistory.add(new CallbackValue.OnStopTetheringFailed(error)); 173 // Call the parent method so that the coverage linter sees it: http://b/385014495 174 TetheringManager.StopTetheringCallback.super.onStopTetheringFailed(error); 175 } 176 177 /** 178 * Verifies that {@link #onStopTetheringSucceeded()} was called 179 */ verifyStopTetheringSucceeded()180 public void verifyStopTetheringSucceeded() { 181 final CallbackValue cv = mHistory.poll(TIMEOUT_MS, c -> true); 182 assertNotNull("No onStopTetheringSucceeded after " + TIMEOUT_MS + " ms", cv); 183 assertTrue("Fail stop tethering:" + cv, 184 cv instanceof CallbackValue.OnStopTetheringSucceeded); 185 } 186 187 /** 188 * Verifies that {@link #onStopTetheringFailed(int)} was called 189 */ expectStopTetheringFailed(final int expected)190 public void expectStopTetheringFailed(final int expected) { 191 final CallbackValue cv = mHistory.poll(TIMEOUT_MS, c -> true); 192 assertNotNull("No onStopTetheringFailed after " + TIMEOUT_MS + " ms", cv); 193 assertTrue("Expect fail with error code " + expected + ", but received: " + cv, 194 (cv instanceof CallbackValue.OnStopTetheringFailed) && (cv.error == expected)); 195 } 196 } 197 isRegexMatch(final String[] ifaceRegexs, String iface)198 private static boolean isRegexMatch(final String[] ifaceRegexs, String iface) { 199 if (ifaceRegexs == null) fail("ifaceRegexs should not be null"); 200 201 for (String regex : ifaceRegexs) { 202 if (iface.matches(regex)) return true; 203 } 204 205 return false; 206 } 207 isAnyIfaceMatch(final String[] ifaceRegexs, final List<String> ifaces)208 public static boolean isAnyIfaceMatch(final String[] ifaceRegexs, final List<String> ifaces) { 209 if (ifaces == null) return false; 210 211 for (String s : ifaces) { 212 if (isRegexMatch(ifaceRegexs, s)) return true; 213 } 214 215 return false; 216 } 217 getFirstMatchingTetheringInterface(final List<String> regexs, final int type, final Set<TetheringInterface> ifaces)218 private static TetheringInterface getFirstMatchingTetheringInterface(final List<String> regexs, 219 final int type, final Set<TetheringInterface> ifaces) { 220 if (ifaces == null || regexs == null) return null; 221 222 final String[] regexArray = regexs.toArray(new String[0]); 223 for (TetheringInterface iface : ifaces) { 224 if (isRegexMatch(regexArray, iface.getInterface()) && type == iface.getType()) { 225 return iface; 226 } 227 } 228 229 return null; 230 } 231 232 // Must poll the callback before looking at the member. 233 public static class TestTetheringEventCallback implements TetheringEventCallback { 234 private static final int TIMEOUT_MS = 30_000; 235 236 public enum CallbackType { 237 ON_SUPPORTED, 238 ON_UPSTREAM, 239 ON_TETHERABLE_REGEX, 240 ON_TETHERABLE_IFACES, 241 ON_TETHERED_IFACES, 242 ON_ERROR, 243 ON_CLIENTS, 244 ON_OFFLOAD_STATUS, 245 }; 246 247 public static class CallbackValue { 248 public final CallbackType callbackType; 249 public final Object callbackParam; 250 public final int callbackParam2; 251 CallbackValue(final CallbackType type, final Object param, final int param2)252 private CallbackValue(final CallbackType type, final Object param, final int param2) { 253 this.callbackType = type; 254 this.callbackParam = param; 255 this.callbackParam2 = param2; 256 } 257 } 258 259 private final ArrayTrackRecord<CallbackValue> mHistory = 260 new ArrayTrackRecord<CallbackValue>(); 261 262 private final ArrayTrackRecord<CallbackValue>.ReadHead mCurrent = 263 mHistory.newReadHead(); 264 265 private TetheringInterfaceRegexps mTetherableRegex; 266 private List<String> mTetherableIfaces; 267 private List<String> mTetheredIfaces; 268 private String mErrorIface; 269 private int mErrorCode; 270 271 @Override onTetheringSupported(boolean supported)272 public void onTetheringSupported(boolean supported) { 273 mHistory.add(new CallbackValue(CallbackType.ON_SUPPORTED, null, (supported ? 1 : 0))); 274 } 275 276 @Override onUpstreamChanged(Network network)277 public void onUpstreamChanged(Network network) { 278 mHistory.add(new CallbackValue(CallbackType.ON_UPSTREAM, network, 0)); 279 } 280 281 @Override onTetherableInterfaceRegexpsChanged(TetheringInterfaceRegexps reg)282 public void onTetherableInterfaceRegexpsChanged(TetheringInterfaceRegexps reg) { 283 mTetherableRegex = reg; 284 mHistory.add(new CallbackValue(CallbackType.ON_TETHERABLE_REGEX, reg, 0)); 285 } 286 287 @Override onTetherableInterfacesChanged(List<String> interfaces)288 public void onTetherableInterfacesChanged(List<String> interfaces) { 289 mTetherableIfaces = interfaces; 290 } 291 // Call the interface default implementation, which will call 292 // onTetherableInterfacesChanged(List<String>). This ensures that the default implementation 293 // of the new callback method calls the old callback method and avoids the need to convert 294 // Set<TetheringInterface> to List<String> in this code. 295 @Override onTetherableInterfacesChanged(Set<TetheringInterface> interfaces)296 public void onTetherableInterfacesChanged(Set<TetheringInterface> interfaces) { 297 TetheringEventCallback.super.onTetherableInterfacesChanged(interfaces); 298 assertHasAllTetheringInterfaces(interfaces, mTetherableIfaces); 299 mHistory.add(new CallbackValue(CallbackType.ON_TETHERABLE_IFACES, interfaces, 0)); 300 } 301 302 @Override onTetheredInterfacesChanged(List<String> interfaces)303 public void onTetheredInterfacesChanged(List<String> interfaces) { 304 mTetheredIfaces = interfaces; 305 } 306 307 @Override onTetheredInterfacesChanged(Set<TetheringInterface> interfaces)308 public void onTetheredInterfacesChanged(Set<TetheringInterface> interfaces) { 309 TetheringEventCallback.super.onTetheredInterfacesChanged(interfaces); 310 assertHasAllTetheringInterfaces(interfaces, mTetheredIfaces); 311 mHistory.add(new CallbackValue(CallbackType.ON_TETHERED_IFACES, interfaces, 0)); 312 } 313 314 @Override onError(String ifName, int error)315 public void onError(String ifName, int error) { 316 mErrorIface = ifName; 317 mErrorCode = error; 318 } 319 320 @Override onError(TetheringInterface ifName, int error)321 public void onError(TetheringInterface ifName, int error) { 322 TetheringEventCallback.super.onError(ifName, error); 323 assertEquals(ifName.getInterface(), mErrorIface); 324 assertEquals(error, mErrorCode); 325 mHistory.add(new CallbackValue(CallbackType.ON_ERROR, ifName, error)); 326 } 327 328 @Override onClientsChanged(Collection<TetheredClient> clients)329 public void onClientsChanged(Collection<TetheredClient> clients) { 330 mHistory.add(new CallbackValue(CallbackType.ON_CLIENTS, clients, 0)); 331 } 332 333 @Override onOffloadStatusChanged(int status)334 public void onOffloadStatusChanged(int status) { 335 mHistory.add(new CallbackValue(CallbackType.ON_OFFLOAD_STATUS, status, 0)); 336 } 337 assertHasAllTetheringInterfaces(Set<TetheringInterface> tetheringIfaces, List<String> ifaces)338 private void assertHasAllTetheringInterfaces(Set<TetheringInterface> tetheringIfaces, 339 List<String> ifaces) { 340 // This does not check that the interfaces are the same. This checks that the 341 // List<String> has all the interface names contained by the Set<TetheringInterface>. 342 assertEquals(tetheringIfaces.size(), ifaces.size()); 343 for (TetheringInterface tether : tetheringIfaces) { 344 assertTrue("iface " + tether.getInterface() 345 + " seen by new callback but not old callback", 346 ifaces.contains(tether.getInterface())); 347 } 348 } 349 expectTetherableInterfacesChanged(@onNull final List<String> regexs, final int type)350 public void expectTetherableInterfacesChanged(@NonNull final List<String> regexs, 351 final int type) { 352 assertNotNull("No expected tetherable ifaces callback", mCurrent.poll(TIMEOUT_MS, 353 (cv) -> { 354 if (cv.callbackType != CallbackType.ON_TETHERABLE_IFACES) return false; 355 final Set<TetheringInterface> interfaces = 356 (Set<TetheringInterface>) cv.callbackParam; 357 return getFirstMatchingTetheringInterface(regexs, type, interfaces) != null; 358 })); 359 } 360 expectNoTetheringActive()361 public void expectNoTetheringActive() { 362 assertNotNull("At least one tethering type unexpectedly active", 363 mCurrent.poll(TIMEOUT_MS, (cv) -> { 364 if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) return false; 365 366 return ((Set<TetheringInterface>) cv.callbackParam).isEmpty(); 367 })); 368 } 369 370 @Nullable pollTetheredInterfacesChanged( @onNull final List<String> regexs, final int type, long timeOutMs)371 public TetheringInterface pollTetheredInterfacesChanged( 372 @NonNull final List<String> regexs, final int type, long timeOutMs) { 373 while (true) { 374 final CallbackValue cv = mCurrent.poll(timeOutMs, c -> true); 375 if (cv == null) return null; 376 377 if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) continue; 378 379 final Set<TetheringInterface> interfaces = 380 (Set<TetheringInterface>) cv.callbackParam; 381 382 final TetheringInterface iface = 383 getFirstMatchingTetheringInterface(regexs, type, interfaces); 384 385 if (iface != null) return iface; 386 } 387 } 388 389 @NonNull expectTetheredInterfacesChanged( @onNull final List<String> regexs, final int type)390 public TetheringInterface expectTetheredInterfacesChanged( 391 @NonNull final List<String> regexs, final int type) { 392 final TetheringInterface iface = pollTetheredInterfacesChanged(regexs, type, 393 TIMEOUT_MS); 394 395 if (iface == null) { 396 fail("No expected tethered ifaces callback, expected type: " + type); 397 } 398 399 return iface; 400 } 401 expectCallbackStarted()402 public void expectCallbackStarted() { 403 // This method uses its own readhead because it just check whether last tethering status 404 // is updated after TetheringEventCallback get registered but do not check content 405 // of received callbacks. Using shared readhead (mCurrent) only when the callbacks the 406 // method polled is also not necessary for other methods which using shared readhead. 407 // All of methods using mCurrent is order mattered. 408 final ArrayTrackRecord<CallbackValue>.ReadHead history = 409 mHistory.newReadHead(); 410 int receivedBitMap = 0; 411 // The each bit represent a type from CallbackType.ON_*. 412 // Expect all of callbacks except for ON_ERROR. 413 final int expectedBitMap = 0xff ^ (1 << CallbackType.ON_ERROR.ordinal()); 414 // Receive ON_ERROR on started callback is not matter. It just means tethering is 415 // failed last time, should able to continue the test this time. 416 while ((receivedBitMap & expectedBitMap) != expectedBitMap) { 417 final CallbackValue cv = history.poll(TIMEOUT_MS, c -> true); 418 if (cv == null) { 419 fail("No expected callbacks, " + "expected bitmap: " 420 + expectedBitMap + ", actual: " + receivedBitMap); 421 } 422 423 receivedBitMap |= (1 << cv.callbackType.ordinal()); 424 } 425 } 426 expectOneOfOffloadStatusChanged(int... offloadStatuses)427 public void expectOneOfOffloadStatusChanged(int... offloadStatuses) { 428 assertNotNull("No offload status changed", mCurrent.poll(TIMEOUT_MS, (cv) -> { 429 if (cv.callbackType != CallbackType.ON_OFFLOAD_STATUS) return false; 430 431 final int status = (int) cv.callbackParam; 432 for (int offloadStatus : offloadStatuses) { 433 if (offloadStatus == status) return true; 434 } 435 436 return false; 437 })); 438 } 439 expectErrorOrTethered(final TetheringInterface iface)440 public void expectErrorOrTethered(final TetheringInterface iface) { 441 assertNotNull("No expected callback", mCurrent.poll(TIMEOUT_MS, (cv) -> { 442 if (cv.callbackType == CallbackType.ON_ERROR 443 && iface.equals((TetheringInterface) cv.callbackParam)) { 444 return true; 445 } 446 if (cv.callbackType == CallbackType.ON_TETHERED_IFACES 447 && ((Set<TetheringInterface>) cv.callbackParam).contains(iface)) { 448 return true; 449 } 450 451 return false; 452 })); 453 } 454 getCurrentValidUpstream()455 public Network getCurrentValidUpstream() { 456 final CallbackValue result = mCurrent.poll(TIMEOUT_MS, (cv) -> { 457 return (cv.callbackType == CallbackType.ON_UPSTREAM) 458 && cv.callbackParam != null; 459 }); 460 461 assertNotNull("No valid upstream", result); 462 return (Network) result.callbackParam; 463 } 464 assumeTetheringSupported()465 public void assumeTetheringSupported() { 466 assumeTrue(isTetheringSupported()); 467 } 468 isTetheringSupported()469 private boolean isTetheringSupported() { 470 final ArrayTrackRecord<CallbackValue>.ReadHead history = 471 mHistory.newReadHead(); 472 final CallbackValue result = history.poll(TIMEOUT_MS, (cv) -> { 473 return cv.callbackType == CallbackType.ON_SUPPORTED; 474 }); 475 476 assertNotNull("No onSupported callback", result); 477 return result.callbackParam2 == 1 /* supported */; 478 } 479 assumeWifiTetheringSupported(final Context ctx)480 public void assumeWifiTetheringSupported(final Context ctx) throws Exception { 481 assumeTrue(isWifiTetheringSupported(ctx)); 482 } 483 isWifiTetheringSupported(final Context ctx)484 public boolean isWifiTetheringSupported(final Context ctx) throws Exception { 485 return isTetheringSupported() 486 && !getTetheringInterfaceRegexps().getTetherableWifiRegexs().isEmpty() 487 && isPortableHotspotSupported(ctx); 488 } 489 getTetheringInterfaceRegexps()490 public TetheringInterfaceRegexps getTetheringInterfaceRegexps() { 491 return mTetherableRegex; 492 } 493 } 494 isWifiEnabled(final WifiManager wm)495 private static boolean isWifiEnabled(final WifiManager wm) { 496 return runAsShell(ACCESS_WIFI_STATE, () -> wm.isWifiEnabled()); 497 498 } 499 waitForWifiEnabled(final Context ctx)500 private static void waitForWifiEnabled(final Context ctx) throws Exception { 501 WifiManager wm = ctx.getSystemService(WifiManager.class); 502 if (isWifiEnabled(wm)) return; 503 504 final ConditionVariable mWaiting = new ConditionVariable(); 505 final BroadcastReceiver receiver = new BroadcastReceiver() { 506 @Override 507 public void onReceive(Context context, Intent intent) { 508 String action = intent.getAction(); 509 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { 510 if (isWifiEnabled(wm)) mWaiting.open(); 511 } 512 } 513 }; 514 try { 515 ctx.registerReceiver(receiver, new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION)); 516 if (!mWaiting.block(DEFAULT_TIMEOUT_MS)) { 517 assertTrue("Wifi did not become enabled after " + DEFAULT_TIMEOUT_MS + "ms", 518 isWifiEnabled(wm)); 519 } 520 } finally { 521 ctx.unregisterReceiver(receiver); 522 } 523 } 524 registerTetheringEventCallback()525 public TestTetheringEventCallback registerTetheringEventCallback() { 526 final TestTetheringEventCallback tetherEventCallback = 527 new TestTetheringEventCallback(); 528 529 runAsShell(ACCESS_NETWORK_STATE, NETWORK_SETTINGS, () -> { 530 mTm.registerTetheringEventCallback(c -> c.run() /* executor */, tetherEventCallback); 531 tetherEventCallback.expectCallbackStarted(); 532 }); 533 534 return tetherEventCallback; 535 } 536 unregisterTetheringEventCallback(final TestTetheringEventCallback callback)537 public void unregisterTetheringEventCallback(final TestTetheringEventCallback callback) { 538 runAsShell(ACCESS_NETWORK_STATE, () -> mTm.unregisterTetheringEventCallback(callback)); 539 } 540 getWifiTetherableInterfaceRegexps( final TestTetheringEventCallback callback)541 private static List<String> getWifiTetherableInterfaceRegexps( 542 final TestTetheringEventCallback callback) { 543 return callback.getTetheringInterfaceRegexps().getTetherableWifiRegexs(); 544 } 545 546 /* Returns if wifi supports hotspot. */ isPortableHotspotSupported(final Context ctx)547 private static boolean isPortableHotspotSupported(final Context ctx) throws Exception { 548 final PackageManager pm = ctx.getPackageManager(); 549 if (!pm.hasSystemFeature(PackageManager.FEATURE_WIFI)) return false; 550 final WifiManager wm = ctx.getSystemService(WifiManager.class); 551 // Wifi feature flags only work when wifi is on. 552 final boolean previousWifiEnabledState = isWifiEnabled(wm); 553 try { 554 if (!previousWifiEnabledState) SystemUtil.runShellCommand("svc wifi enable"); 555 waitForWifiEnabled(ctx); 556 return runAsShell(ACCESS_WIFI_STATE, () -> wm.isPortableHotspotSupported()); 557 } finally { 558 if (!previousWifiEnabledState) { 559 new CtsNetUtils(ctx).disableWifi(); 560 } 561 } 562 } 563 564 /** 565 * Starts Wi-Fi tethering with TETHER_PRIVILEGED permission. 566 */ startWifiTethering(final TestTetheringEventCallback callback)567 public TetheringInterface startWifiTethering(final TestTetheringEventCallback callback) { 568 return startWifiTethering(callback, null); 569 } 570 571 /** 572 * Starts Wi-Fi tethering with TETHER_PRIVILEGED permission and the specified 573 * SoftApConfiguration. 574 */ startWifiTethering(final TestTetheringEventCallback callback, final SoftApConfiguration softApConfiguration)575 public TetheringInterface startWifiTethering(final TestTetheringEventCallback callback, 576 final SoftApConfiguration softApConfiguration) { 577 return runAsShell(TETHER_PRIVILEGED, () -> startWifiTetheringNoPermissions( 578 callback, softApConfiguration)); 579 } 580 581 /** 582 * Starts Wi-Fi tethering without any permission with the specified SoftApConfiguration. 583 */ startWifiTetheringNoPermissions( final TestTetheringEventCallback callback, final SoftApConfiguration softApConfiguration)584 public TetheringInterface startWifiTetheringNoPermissions( 585 final TestTetheringEventCallback callback, 586 final SoftApConfiguration softApConfiguration) { 587 final List<String> wifiRegexs = getWifiTetherableInterfaceRegexps(callback); 588 589 final StartTetheringCallback startTetheringCallback = new StartTetheringCallback(); 590 TetheringRequest.Builder builder = new TetheringRequest.Builder(TETHERING_WIFI) 591 .setShouldShowEntitlementUi(false); 592 if (softApConfiguration != null) { 593 builder.setSoftApConfiguration(softApConfiguration); 594 } 595 final TetheringRequest request = builder.build(); 596 597 mTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback); 598 startTetheringCallback.verifyTetheringStarted(); 599 600 final TetheringInterface iface = 601 callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI); 602 603 callback.expectOneOfOffloadStatusChanged( 604 TETHER_HARDWARE_OFFLOAD_STARTED, 605 TETHER_HARDWARE_OFFLOAD_FAILED); 606 607 return iface; 608 } 609 610 private static class StopSoftApCallback implements SoftApCallback { 611 private final ConditionVariable mWaiting = new ConditionVariable(); 612 @Override onStateChanged(int state, int failureReason)613 public void onStateChanged(int state, int failureReason) { 614 if (state == WifiManager.WIFI_AP_STATE_DISABLED) mWaiting.open(); 615 } 616 617 @Override onConnectedClientsChanged(List<WifiClient> clients)618 public void onConnectedClientsChanged(List<WifiClient> clients) { } 619 waitForSoftApStopped()620 public void waitForSoftApStopped() { 621 if (!mWaiting.block(DEFAULT_TIMEOUT_MS)) { 622 fail("stopSoftAp Timeout"); 623 } 624 } 625 } 626 627 // Wait for softAp to be disabled. This is necessary on devices where stopping softAp 628 // deletes the interface. On these devices, tethering immediately stops when the softAp 629 // interface is removed, but softAp is not yet fully disabled. Wait for softAp to be 630 // fully disabled, because otherwise the next test might fail because it attempts to 631 // start softAp before it's fully stopped. expectSoftApDisabled()632 public void expectSoftApDisabled() { 633 final StopSoftApCallback callback = new StopSoftApCallback(); 634 try { 635 runAsShell(NETWORK_SETTINGS, () -> mWm.registerSoftApCallback(c -> c.run(), callback)); 636 // registerSoftApCallback will immediately call the callback with the current state, so 637 // this callback will fire even if softAp is already disabled. 638 callback.waitForSoftApStopped(); 639 } finally { 640 runAsShell(NETWORK_SETTINGS, () -> mWm.unregisterSoftApCallback(callback)); 641 } 642 } 643 stopWifiTethering(final TestTetheringEventCallback callback)644 public void stopWifiTethering(final TestTetheringEventCallback callback) { 645 runAsShell(TETHER_PRIVILEGED, () -> { 646 mTm.stopTethering(TETHERING_WIFI); 647 callback.expectNoTetheringActive(); 648 callback.expectOneOfOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); 649 }); 650 expectSoftApDisabled(); 651 } 652 653 /** 654 * Calls {@link TetheringManager#stopTethering(TetheringRequest, Executor, 655 * TetheringManager.StopTetheringCallback)} and verifies if it succeeded or failed. 656 */ stopTethering(final TetheringRequest request, boolean expectSuccess)657 public void stopTethering(final TetheringRequest request, boolean expectSuccess) { 658 final StopTetheringCallback stopTetheringCallback = new StopTetheringCallback(); 659 runAsShell(TETHER_PRIVILEGED, () -> { 660 mTm.stopTethering(request, c -> c.run() /* executor */, stopTetheringCallback); 661 if (expectSuccess) { 662 stopTetheringCallback.verifyStopTetheringSucceeded(); 663 } else { 664 stopTetheringCallback.expectStopTetheringFailed(TETHER_ERROR_UNKNOWN_REQUEST); 665 } 666 }); 667 } 668 stopAllTethering()669 public void stopAllTethering() { 670 final TestTetheringEventCallback callback = registerTetheringEventCallback(); 671 try { 672 runAsShell(TETHER_PRIVILEGED, () -> { 673 mTm.stopAllTethering(); 674 callback.expectNoTetheringActive(); 675 }); 676 } finally { 677 unregisterTetheringEventCallback(callback); 678 } 679 } 680 } 681