1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server 18 19 import android.bluetooth.BluetoothAdapter 20 import android.bluetooth.BluetoothDevice 21 import android.bluetooth.BluetoothServerSocket 22 import android.bluetooth.BluetoothSocket 23 import android.net.INetworkMonitor 24 import android.net.INetworkMonitorCallbacks 25 import android.net.IpPrefix 26 import android.net.L2capNetworkSpecifier 27 import android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_6LOWPAN 28 import android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_NONE 29 import android.net.L2capNetworkSpecifier.ROLE_CLIENT 30 import android.net.L2capNetworkSpecifier.ROLE_SERVER 31 import android.net.LinkAddress 32 import android.net.LinkProperties 33 import android.net.MacAddress 34 import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED 35 import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED 36 import android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH 37 import android.net.NetworkRequest 38 import android.net.NetworkSpecifier 39 import android.net.RouteInfo 40 import android.os.Build 41 import android.os.Handler 42 import android.os.HandlerThread 43 import android.os.ParcelFileDescriptor 44 import com.android.server.net.L2capNetwork.L2capIpClient 45 import com.android.server.net.L2capPacketForwarder 46 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo 47 import com.android.testutils.DevSdkIgnoreRunner 48 import com.android.testutils.RecorderCallback.CallbackEntry.Lost 49 import com.android.testutils.RecorderCallback.CallbackEntry.Reserved 50 import com.android.testutils.RecorderCallback.CallbackEntry.Unavailable 51 import com.android.testutils.TestableNetworkCallback 52 import com.android.testutils.anyNetwork 53 import com.android.testutils.waitForIdle 54 import java.io.IOException 55 import java.util.Optional 56 import java.util.concurrent.LinkedBlockingQueue 57 import kotlin.test.assertEquals 58 import kotlin.test.assertFalse 59 import kotlin.test.assertNull 60 import org.junit.After 61 import org.junit.Before 62 import org.junit.Test 63 import org.junit.runner.RunWith 64 import org.mockito.ArgumentCaptor 65 import org.mockito.ArgumentMatchers.eq 66 import org.mockito.ArgumentMatchers.isNull 67 import org.mockito.Mockito.doAnswer 68 import org.mockito.Mockito.doReturn 69 import org.mockito.Mockito.doThrow 70 import org.mockito.Mockito.mock 71 import org.mockito.Mockito.verify 72 73 private const val PSM = 0x85 74 private val REMOTE_MAC = byteArrayOf(1, 2, 3, 4, 5, 6) 75 private val REQUEST = NetworkRequest.Builder() 76 .addTransportType(TRANSPORT_BLUETOOTH) 77 .removeCapability(NET_CAPABILITY_TRUSTED) 78 .removeCapability(NET_CAPABILITY_NOT_RESTRICTED) 79 .build() 80 81 @RunWith(DevSdkIgnoreRunner::class) 82 @IgnoreUpTo(Build.VERSION_CODES.R) 83 @DevSdkIgnoreRunner.MonitorThreadLeak 84 class CSL2capProviderTest : CSTest() { 85 private val networkMonitor = mock<INetworkMonitor>() 86 87 private val btAdapter = mock<BluetoothAdapter>() 88 private val btDevice = mock<BluetoothDevice>() 89 private val btServerSocket = mock<BluetoothServerSocket>() 90 private val btSocket = mock<BluetoothSocket>() 91 private val tunInterface = mock<ParcelFileDescriptor>() 92 private val l2capIpClient = mock<L2capIpClient>() 93 private val packetForwarder = mock<L2capPacketForwarder>() 94 private val providerDeps = mock<L2capNetworkProvider.Dependencies>() 95 96 // BlockingQueue does not support put(null) operations, as null is used as an internal sentinel 97 // value. Therefore, use Optional<BluetoothSocket> where an empty optional signals the 98 // BluetoothServerSocket#close() operation. 99 private val acceptQueue = LinkedBlockingQueue<Optional<BluetoothSocket>>() 100 101 private val handlerThread = HandlerThread("CSL2capProviderTest thread").apply { start() } 102 private val registeredCallbacks = ArrayList<TestableNetworkCallback>() 103 104 // Requires Dependencies mock to be setup before creation. 105 private lateinit var provider: L2capNetworkProvider 106 107 @Before 108 fun innerSetUp() { 109 doReturn(btAdapter).`when`(bluetoothManager).getAdapter() 110 doReturn(btServerSocket).`when`(btAdapter).listenUsingInsecureL2capChannel() 111 doReturn(PSM).`when`(btServerSocket).getPsm() 112 doReturn(btDevice).`when`(btAdapter).getRemoteDevice(eq(REMOTE_MAC)) 113 doReturn(btSocket).`when`(btDevice).createInsecureL2capChannel(eq(PSM)) 114 115 doAnswer { 116 val sock = acceptQueue.take() 117 if (sock == null || !sock.isPresent()) throw IOException() 118 sock.get() 119 }.`when`(btServerSocket).accept() 120 121 doAnswer { 122 acceptQueue.put(Optional.empty()) 123 }.`when`(btServerSocket).close() 124 125 doReturn(handlerThread).`when`(providerDeps).getHandlerThread() 126 doReturn(tunInterface).`when`(providerDeps).createTunInterface(any()) 127 doReturn(packetForwarder).`when`(providerDeps) 128 .createL2capPacketForwarder(any(), any(), any(), any(), any()) 129 doReturn(l2capIpClient).`when`(providerDeps).createL2capIpClient(any(), any(), any()) 130 131 val lp = LinkProperties() 132 val ifname = "l2cap-tun0" 133 lp.setInterfaceName(ifname) 134 lp.addLinkAddress(LinkAddress("fe80::1/64")) 135 lp.addRoute(RouteInfo(IpPrefix("fe80::/64"), null /* nextHop */, ifname)) 136 doReturn(lp).`when`(l2capIpClient).start() 137 138 // Note: In order to properly register a NetworkAgent, a NetworkMonitor must be created for 139 // the agent. CSAgentWrapper already does some of this, but requires adding additional 140 // Dependencies to the production code. Create a mocked NM inside this test instead. 141 doAnswer { i -> 142 val cb = i.arguments[2] as INetworkMonitorCallbacks 143 cb.onNetworkMonitorCreated(networkMonitor) 144 }.`when`(networkStack).makeNetworkMonitor( 145 any() /* network */, 146 isNull() /* name */, 147 any() /* callbacks */ 148 ) 149 150 provider = L2capNetworkProvider(providerDeps, context) 151 provider.start() 152 } 153 154 @After 155 fun innerTearDown() { 156 // Unregistering a callback which has previously been unregistered by virtue of receiving 157 // onUnavailable is a noop. 158 registeredCallbacks.forEach { cm.unregisterNetworkCallback(it) } 159 // Wait for CS handler idle, meaning the unregisterNetworkCallback has been processed and 160 // L2capNetworkProvider has been notified. 161 waitForIdle() 162 163 // While quitSafely() effectively waits for idle, it is not enough, because the tear down 164 // path itself posts on the handler thread. This means that waitForIdle() needs to run 165 // twice. The first time, to ensure all active threads have been joined, and the second time 166 // to run all associated clean up actions. 167 handlerThread.waitForIdle(HANDLER_TIMEOUT_MS) 168 handlerThread.quitSafely() 169 handlerThread.join() 170 } 171 172 private fun reserveNetwork(nr: NetworkRequest) = TestableNetworkCallback().also { 173 cm.reserveNetwork(nr, csHandler, it) 174 registeredCallbacks.add(it) 175 } 176 177 private fun requestNetwork(nr: NetworkRequest) = TestableNetworkCallback().also { 178 cm.requestNetwork(nr, it, csHandler) 179 registeredCallbacks.add(it) 180 } 181 182 private fun NetworkRequest.copyWithSpecifier(specifier: NetworkSpecifier): NetworkRequest { 183 // Note: NetworkRequest.Builder(NetworkRequest) *does not* perform a defensive copy but 184 // changes the underlying request. 185 return NetworkRequest.Builder(NetworkRequest(this)) 186 .setNetworkSpecifier(specifier) 187 .build() 188 } 189 190 @Test 191 fun testReservation() { 192 val l2capServerSpecifier = L2capNetworkSpecifier.Builder() 193 .setRole(ROLE_SERVER) 194 .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN) 195 .build() 196 val l2capReservation = REQUEST.copyWithSpecifier(l2capServerSpecifier) 197 val reservationCb = reserveNetwork(l2capReservation) 198 199 val reservedCaps = reservationCb.expect<Reserved>().caps 200 val reservedSpec = reservedCaps.networkSpecifier as L2capNetworkSpecifier 201 202 assertEquals(PSM, reservedSpec.getPsm()) 203 assertEquals(HEADER_COMPRESSION_6LOWPAN, reservedSpec.headerCompression) 204 assertNull(reservedSpec.remoteAddress) 205 206 reservationCb.assertNoCallback() 207 } 208 209 @Test 210 fun testBlanketOffer_reservationWithoutSpecifier() { 211 reserveNetwork(REQUEST).assertNoCallback() 212 } 213 214 @Test 215 fun testBlanketOffer_reservationWithCorrectSpecifier() { 216 var specifier = L2capNetworkSpecifier.Builder() 217 .setRole(ROLE_SERVER) 218 .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN) 219 .build() 220 var nr = REQUEST.copyWithSpecifier(specifier) 221 reserveNetwork(nr).expect<Reserved>() 222 223 specifier = L2capNetworkSpecifier.Builder() 224 .setRole(ROLE_SERVER) 225 .setHeaderCompression(HEADER_COMPRESSION_NONE) 226 .build() 227 nr = REQUEST.copyWithSpecifier(specifier) 228 reserveNetwork(nr).expect<Reserved>() 229 } 230 231 @Test 232 fun testBlanketOffer_reservationWithIncorrectSpecifier() { 233 var specifier = L2capNetworkSpecifier.Builder().build() 234 var nr = REQUEST.copyWithSpecifier(specifier) 235 reserveNetwork(nr).assertNoCallback() 236 237 specifier = L2capNetworkSpecifier.Builder() 238 .setRole(ROLE_SERVER) 239 .build() 240 nr = REQUEST.copyWithSpecifier(specifier) 241 reserveNetwork(nr).assertNoCallback() 242 243 specifier = L2capNetworkSpecifier.Builder() 244 .setRole(ROLE_SERVER) 245 .setHeaderCompression(HEADER_COMPRESSION_NONE) 246 .setPsm(0x81) 247 .build() 248 nr = REQUEST.copyWithSpecifier(specifier) 249 reserveNetwork(nr).assertNoCallback() 250 251 specifier = L2capNetworkSpecifier.Builder() 252 .setHeaderCompression(HEADER_COMPRESSION_NONE) 253 .build() 254 nr = REQUEST.copyWithSpecifier(specifier) 255 reserveNetwork(nr).assertNoCallback() 256 } 257 258 @Test 259 fun testBluetoothException_listenUsingInsecureL2capChannelThrows() { 260 doThrow(IOException()).`when`(btAdapter).listenUsingInsecureL2capChannel() 261 var specifier = L2capNetworkSpecifier.Builder() 262 .setRole(ROLE_SERVER) 263 .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN) 264 .build() 265 var nr = REQUEST.copyWithSpecifier(specifier) 266 reserveNetwork(nr).expect<Unavailable>() 267 268 doReturn(btServerSocket).`when`(btAdapter).listenUsingInsecureL2capChannel() 269 reserveNetwork(nr).expect<Reserved>() 270 } 271 272 @Test 273 fun testBluetoothException_acceptThrows() { 274 doThrow(IOException()).`when`(btServerSocket).accept() 275 var specifier = L2capNetworkSpecifier.Builder() 276 .setRole(ROLE_SERVER) 277 .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN) 278 .build() 279 var nr = REQUEST.copyWithSpecifier(specifier) 280 val cb = reserveNetwork(nr) 281 cb.expect<Reserved>() 282 cb.expect<Unavailable>() 283 284 // BluetoothServerSocket#close() puts Optional.empty() on the acceptQueue. 285 acceptQueue.clear() 286 doAnswer { 287 val sock = acceptQueue.take() 288 assertFalse(sock.isPresent()) 289 throw IOException() // to indicate the socket was closed. 290 }.`when`(btServerSocket).accept() 291 val cb2 = reserveNetwork(nr) 292 cb2.expect<Reserved>() 293 cb2.assertNoCallback() 294 } 295 296 @Test 297 fun testServerNetwork() { 298 val specifier = L2capNetworkSpecifier.Builder() 299 .setRole(ROLE_SERVER) 300 .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN) 301 .build() 302 val nr = REQUEST.copyWithSpecifier(specifier) 303 val cb = reserveNetwork(nr) 304 cb.expect<Reserved>() 305 306 // Unblock BluetoothServerSocket#accept() 307 doReturn(true).`when`(btSocket).isConnected() 308 acceptQueue.put(Optional.of(btSocket)) 309 310 cb.expectAvailableCallbacks(anyNetwork(), validated = false) 311 cb.assertNoCallback() 312 // Verify that packet forwarding was started. 313 // TODO: stop mocking L2capPacketForwarder. 314 verify(providerDeps).createL2capPacketForwarder(any(), any(), any(), any(), any()) 315 } 316 317 @Test 318 fun testBluetoothException_createInsecureL2capChannelThrows() { 319 doThrow(IOException()).`when`(btDevice).createInsecureL2capChannel(any()) 320 321 val specifier = L2capNetworkSpecifier.Builder() 322 .setRole(ROLE_CLIENT) 323 .setHeaderCompression(HEADER_COMPRESSION_NONE) 324 .setRemoteAddress(MacAddress.fromBytes(REMOTE_MAC)) 325 .setPsm(PSM) 326 .build() 327 val nr = REQUEST.copyWithSpecifier(specifier) 328 val cb = requestNetwork(nr) 329 330 cb.expect<Unavailable>() 331 } 332 333 @Test 334 fun testBluetoothException_bluetoothSocketConnectThrows() { 335 doThrow(IOException()).`when`(btSocket).connect() 336 337 val specifier = L2capNetworkSpecifier.Builder() 338 .setRole(ROLE_CLIENT) 339 .setHeaderCompression(HEADER_COMPRESSION_NONE) 340 .setRemoteAddress(MacAddress.fromBytes(REMOTE_MAC)) 341 .setPsm(PSM) 342 .build() 343 val nr = REQUEST.copyWithSpecifier(specifier) 344 val cb = requestNetwork(nr) 345 346 cb.expect<Unavailable>() 347 } 348 349 @Test 350 fun testClientNetwork() { 351 val specifier = L2capNetworkSpecifier.Builder() 352 .setRole(ROLE_CLIENT) 353 .setHeaderCompression(HEADER_COMPRESSION_NONE) 354 .setRemoteAddress(MacAddress.fromBytes(REMOTE_MAC)) 355 .setPsm(PSM) 356 .build() 357 val nr = REQUEST.copyWithSpecifier(specifier) 358 val cb = requestNetwork(nr) 359 cb.expectAvailableCallbacks(anyNetwork(), validated = false) 360 } 361 362 @Test 363 fun testClientNetwork_headerCompressionMismatch() { 364 var specifier = L2capNetworkSpecifier.Builder() 365 .setRole(ROLE_CLIENT) 366 .setHeaderCompression(HEADER_COMPRESSION_NONE) 367 .setRemoteAddress(MacAddress.fromBytes(REMOTE_MAC)) 368 .setPsm(PSM) 369 .build() 370 var nr = REQUEST.copyWithSpecifier(specifier) 371 val cb = requestNetwork(nr) 372 cb.expectAvailableCallbacks(anyNetwork(), validated = false) 373 374 specifier = L2capNetworkSpecifier.Builder() 375 .setRole(ROLE_CLIENT) 376 .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN) 377 .setRemoteAddress(MacAddress.fromBytes(REMOTE_MAC)) 378 .setPsm(PSM) 379 .build() 380 nr = REQUEST.copyWithSpecifier(specifier) 381 val cb2 = requestNetwork(nr) 382 cb2.expect<Unavailable>() 383 } 384 385 @Test 386 fun testClientNetwork_multipleRequests() { 387 val specifier = L2capNetworkSpecifier.Builder() 388 .setRole(ROLE_CLIENT) 389 .setHeaderCompression(HEADER_COMPRESSION_NONE) 390 .setRemoteAddress(MacAddress.fromBytes(REMOTE_MAC)) 391 .setPsm(PSM) 392 .build() 393 val nr = REQUEST.copyWithSpecifier(specifier) 394 val cb = requestNetwork(nr) 395 cb.expectAvailableCallbacks(anyNetwork(), validated = false) 396 397 val cb2 = requestNetwork(nr) 398 cb2.expectAvailableCallbacks(anyNetwork(), validated = false) 399 } 400 401 /** Test to ensure onLost() is sent before onUnavailable() when the network is torn down. */ 402 @Test 403 fun testClientNetwork_gracefulTearDown() { 404 val specifier = L2capNetworkSpecifier.Builder() 405 .setRole(ROLE_CLIENT) 406 .setHeaderCompression(HEADER_COMPRESSION_NONE) 407 .setRemoteAddress(MacAddress.fromBytes(REMOTE_MAC)) 408 .setPsm(PSM) 409 .build() 410 411 val nr = REQUEST.copyWithSpecifier(specifier) 412 val cb = requestNetwork(nr) 413 cb.expectAvailableCallbacks(anyNetwork(), validated = false) 414 415 // Capture the L2capPacketForwarder callback object to tear down the network. 416 val handlerCaptor = ArgumentCaptor.forClass(Handler::class.java) 417 val forwarderCbCaptor = ArgumentCaptor.forClass(L2capPacketForwarder.ICallback::class.java) 418 verify(providerDeps).createL2capPacketForwarder( 419 handlerCaptor.capture(), any(), any(), any(), forwarderCbCaptor.capture()) 420 val handler = handlerCaptor.value 421 val forwarderCb = forwarderCbCaptor.value 422 423 // Trigger a forwarding error 424 handler.post { forwarderCb.onError() } 425 handler.waitForIdle(HANDLER_TIMEOUT_MS) 426 427 cb.expect<Lost>() 428 cb.expect<Unavailable>() 429 } 430 } 431