• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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