Add a basic CSTest for L2capNetworkProvider
This moves the tests that used to be in L2capNetworkProviderTest into a
CSTest based CSL2capProviderTest; however, it still only tests that
reservation offers are created in response to reservation requests.
More thorough tests will be added in follow ups.
Test: TH
Change-Id: I0b243f4517ee41376716cf4930f90657c4bc77b0
diff --git a/service/src/com/android/server/L2capNetworkProvider.java b/service/src/com/android/server/L2capNetworkProvider.java
index e3becd6..57fd830 100644
--- a/service/src/com/android/server/L2capNetworkProvider.java
+++ b/service/src/com/android/server/L2capNetworkProvider.java
@@ -655,11 +655,6 @@
@VisibleForTesting
public static class Dependencies {
- /** Get NetworkProvider */
- public NetworkProvider getNetworkProvider(Context context, Looper looper) {
- return new NetworkProvider(context, looper, TAG);
- }
-
/** Get the HandlerThread for L2capNetworkProvider to run on */
public HandlerThread getHandlerThread() {
final HandlerThread thread = new HandlerThread("L2capNetworkProviderThread");
@@ -678,7 +673,7 @@
mContext = context;
mHandlerThread = mDeps.getHandlerThread();
mHandler = new Handler(mHandlerThread.getLooper());
- mProvider = mDeps.getNetworkProvider(context, mHandlerThread.getLooper());
+ mProvider = new NetworkProvider(context, mHandlerThread.getLooper(), TAG);
mBlanketOffer = new BlanketReservationOffer();
mClientOffer = new ClientOffer();
}
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSL2capProviderTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSL2capProviderTest.kt
new file mode 100644
index 0000000..d44bd0a
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivityservice/CSL2capProviderTest.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server
+
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothManager
+import android.bluetooth.BluetoothServerSocket
+import android.bluetooth.BluetoothSocket
+import android.content.Context
+import android.net.L2capNetworkSpecifier
+import android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_6LOWPAN
+import android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_NONE
+import android.net.L2capNetworkSpecifier.ROLE_SERVER
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED
+import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
+import android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH
+import android.net.NetworkProvider
+import android.net.NetworkProvider.NetworkOfferCallback
+import android.net.NetworkRequest
+import android.net.NetworkScore
+import android.net.NetworkSpecifier
+import android.os.Build
+import android.os.HandlerThread
+import android.os.Looper
+import com.android.server.L2capNetworkProvider
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.RecorderCallback.CallbackEntry.Reserved
+import com.android.testutils.TestableNetworkCallback
+import java.io.IOException
+import java.util.concurrent.Executor
+import java.util.concurrent.LinkedBlockingQueue
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.doAnswer
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
+
+private const val PSM = 0x85
+private val REMOTE_MAC = byteArrayOf(1, 2, 3, 4, 5, 6)
+private val REQUEST = NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_BLUETOOTH)
+ .removeCapability(NET_CAPABILITY_TRUSTED)
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .build()
+
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.R)
+class CSL2capProviderTest : CSTest() {
+ private val btAdapter = mock<BluetoothAdapter>()
+ private val btServerSocket = mock<BluetoothServerSocket>()
+ private val btSocket = mock<BluetoothSocket>()
+ private val providerDeps = mock<L2capNetworkProvider.Dependencies>()
+ private val acceptQueue = LinkedBlockingQueue<BluetoothSocket>()
+
+ private val handlerThread = HandlerThread("CSL2capProviderTest thread").apply { start() }
+
+ // Requires Dependencies mock to be setup before creation.
+ private lateinit var provider: L2capNetworkProvider
+
+ @Before
+ fun innerSetUp() {
+ doReturn(btAdapter).`when`(bluetoothManager).getAdapter()
+ doReturn(btServerSocket).`when`(btAdapter).listenUsingInsecureL2capChannel()
+ doReturn(PSM).`when`(btServerSocket).getPsm();
+
+ doAnswer {
+ val sock = acceptQueue.take()
+ sock ?: throw IOException()
+ }.`when`(btServerSocket).accept()
+
+ doAnswer {
+ acceptQueue.put(null)
+ }.`when`(btServerSocket).close()
+
+ doReturn(handlerThread).`when`(providerDeps).getHandlerThread()
+ provider = L2capNetworkProvider(providerDeps, context)
+ provider.start()
+ }
+
+ @After
+ fun innerTearDown() {
+ handlerThread.quitSafely()
+ handlerThread.join()
+ }
+
+ private fun reserveNetwork(nr: NetworkRequest) = TestableNetworkCallback().also {
+ cm.reserveNetwork(nr, csHandler, it)
+ }
+
+ private fun requestNetwork(nr: NetworkRequest) = TestableNetworkCallback().also {
+ cm.requestNetwork(nr, it, csHandler)
+ }
+
+ private fun NetworkRequest.copyWithSpecifier(specifier: NetworkSpecifier): NetworkRequest {
+ // Note: NetworkRequest.Builder(NetworkRequest) *does not* perform a defensive copy but
+ // changes the underlying request.
+ return NetworkRequest.Builder(NetworkRequest(this))
+ .setNetworkSpecifier(specifier)
+ .build()
+ }
+
+ @Test
+ fun testReservation() {
+ val l2capServerSpecifier = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN)
+ .build()
+ val l2capReservation = REQUEST.copyWithSpecifier(l2capServerSpecifier)
+ val reservationCb = reserveNetwork(l2capReservation)
+
+ val reservedCaps = reservationCb.expect<Reserved>().caps
+ val reservedSpec = reservedCaps.networkSpecifier as L2capNetworkSpecifier
+
+ assertEquals(PSM, reservedSpec.getPsm())
+ assertEquals(HEADER_COMPRESSION_6LOWPAN, reservedSpec.headerCompression)
+ assertNull(reservedSpec.remoteAddress)
+
+ reservationCb.assertNoCallback()
+ }
+
+ @Test
+ fun testBlanketOffer_reservationWithoutSpecifier() {
+ reserveNetwork(REQUEST).assertNoCallback()
+ }
+
+ @Test
+ fun testBlanketOffer_reservationWithCorrectSpecifier() {
+ var specifier = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN)
+ .build()
+ var nr = REQUEST.copyWithSpecifier(specifier)
+ reserveNetwork(nr).expect<Reserved>()
+
+ specifier = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .setHeaderCompression(HEADER_COMPRESSION_NONE)
+ .build()
+ nr = REQUEST.copyWithSpecifier(specifier)
+ reserveNetwork(nr).expect<Reserved>()
+ }
+
+ @Test
+ fun testBlanketOffer_reservationWithIncorrectSpecifier() {
+ var specifier = L2capNetworkSpecifier.Builder().build()
+ var nr = REQUEST.copyWithSpecifier(specifier)
+ reserveNetwork(nr).assertNoCallback()
+
+ specifier = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .build()
+ nr = REQUEST.copyWithSpecifier(specifier)
+ reserveNetwork(nr).assertNoCallback()
+
+ specifier = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .setHeaderCompression(HEADER_COMPRESSION_NONE)
+ .setPsm(0x81)
+ .build()
+ nr = REQUEST.copyWithSpecifier(specifier)
+ reserveNetwork(nr).assertNoCallback()
+
+ specifier = L2capNetworkSpecifier.Builder()
+ .setHeaderCompression(HEADER_COMPRESSION_NONE)
+ .build()
+ nr = REQUEST.copyWithSpecifier(specifier)
+ reserveNetwork(nr).assertNoCallback()
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
index 77c7fe6..350a140 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
@@ -18,6 +18,7 @@
import android.app.AlarmManager
import android.app.AppOpsManager
+import android.bluetooth.BluetoothManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@@ -209,6 +210,7 @@
doReturn(true).`when`(it).isDataCapable()
}
val subscriptionManager = mock<SubscriptionManager>()
+ val bluetoothManager = mock<BluetoothManager>()
val multicastRoutingCoordinatorService = mock<MulticastRoutingCoordinatorService>()
val satelliteAccessController = mock<SatelliteAccessController>()
@@ -505,6 +507,7 @@
Context.BATTERY_STATS_SERVICE -> batteryManager
Context.STATS_MANAGER -> null // Stats manager is final and can't be mocked
Context.APP_OPS_SERVICE -> appOpsManager
+ Context.BLUETOOTH_SERVICE -> bluetoothManager
else -> super.getSystemService(serviceName)
}
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
index 8ff790c..6b560de 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
@@ -23,6 +23,7 @@
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.PackageManager.FEATURE_BLUETOOTH
+import android.content.pm.PackageManager.FEATURE_BLUETOOTH_LE
import android.content.pm.PackageManager.FEATURE_ETHERNET
import android.content.pm.PackageManager.FEATURE_WIFI
import android.content.pm.PackageManager.FEATURE_WIFI_DIRECT
@@ -103,7 +104,13 @@
}
internal fun makeMockPackageManager(realContext: Context) = mock<PackageManager>().also { pm ->
- val supported = listOf(FEATURE_WIFI, FEATURE_WIFI_DIRECT, FEATURE_BLUETOOTH, FEATURE_ETHERNET)
+ val supported = listOf(
+ FEATURE_WIFI,
+ FEATURE_WIFI_DIRECT,
+ FEATURE_BLUETOOTH,
+ FEATURE_BLUETOOTH_LE,
+ FEATURE_ETHERNET
+ )
doReturn(true).`when`(pm).hasSystemFeature(argThat { supported.contains(it) })
val myPackageName = realContext.packageName
val myPackageInfo = realContext.packageManager.getPackageInfo(myPackageName,