diff options
9 files changed, 260 insertions, 60 deletions
diff --git a/service/java/com/android/server/wifi/ActiveModeWarden.java b/service/java/com/android/server/wifi/ActiveModeWarden.java index ef2aa9ea1..457b68773 100644 --- a/service/java/com/android/server/wifi/ActiveModeWarden.java +++ b/service/java/com/android/server/wifi/ActiveModeWarden.java @@ -71,6 +71,7 @@ public class ActiveModeWarden { private final WifiPermissionsUtil mWifiPermissionsUtil; private final BatteryStatsManager mBatteryStatsManager; private final ScanRequestProxy mScanRequestProxy; + private final WifiNative mWifiNative; private final WifiController mWifiController; private WifiManager.SoftApCallback mSoftApCallback; @@ -117,6 +118,7 @@ public class ActiveModeWarden { mDefaultModeManager = defaultModeManager; mBatteryStatsManager = batteryStatsManager; mScanRequestProxy = wifiInjector.getScanRequestProxy(); + mWifiNative = wifiNative; mWifiController = new WifiController(); wifiNative.registerStatusListener(isReady -> { @@ -164,22 +166,8 @@ public class ActiveModeWarden { * @return Returns whether the device can support at least one concurrent client mode manager & * softap manager. */ - public boolean canSupportAtleastOneConcurrentClientAndSoftApManager() { - // We already have 1 client mode manager and 1 softap manager active, so yes. - if (hasAnyClientModeManager() && hasAnySoftApManager()) { - return true; - } - // We already have 1 client mode manager active, check if we can create a softap manager. - if (hasAnyClientModeManager()) { - return mCanRequestMoreSoftApManagers; - } - // We already have 1 softap manager active, check if we can create a client mode manager. - if (hasAnySoftApManager()) { - return mCanRequestMoreClientModeManagers; - } - // We don't have any active mode manager, this can happen if wifi is fully off. We return - // false here because we cannot retrieve this info from the HAL. - return false; + public boolean isStaApConcurrencySupported() { + return mWifiNative.isStaApConcurrencySupported(); } /** Begin listening to broadcasts and start the internal state machine. */ diff --git a/service/java/com/android/server/wifi/HalDeviceManager.java b/service/java/com/android/server/wifi/HalDeviceManager.java index be3f6af4d..d85c4893b 100644 --- a/service/java/com/android/server/wifi/HalDeviceManager.java +++ b/service/java/com/android/server/wifi/HalDeviceManager.java @@ -512,6 +512,29 @@ public class HalDeviceManager { void onRttControllerDestroyed(); } + /** + * Returns whether the provided Iface combo can be supported by the device. + * Note: This only returns an answer based on the iface combination exposed by the HAL. + * The actual iface creation/deletion rules depend on the iface priorities set in + * {@link #allowedToDeleteIfaceTypeForRequestedType(int, int, WifiIfaceInfo[][], int)} + * + * @param ifaceCombo SparseArray keyed in by the iface type to number of ifaces needed. + * @return true if the device supports the provided combo, false otherwise. + */ + public boolean canSupportIfaceCombo(SparseArray<Integer> ifaceCombo) { + if (VDBG) Log.d(TAG, "canSupportIfaceCombo: ifaceCombo=" + ifaceCombo); + + synchronized (mLock) { + int[] ifaceComboArr = new int[IFACE_TYPES_BY_PRIORITY.length]; + for (int type : IFACE_TYPES_BY_PRIORITY) { + ifaceComboArr[type] = ifaceCombo.get(type, 0); + } + WifiChipInfo[] chipInfos = getAllChipInfo(); + if (chipInfos == null) return false; + return isItPossibleToCreateIfaceCombo(chipInfos, ifaceComboArr); + } + } + // internal state /* This "PRIORITY" is not for deciding interface elimination (that is controlled by @@ -543,6 +566,7 @@ public class HalDeviceManager { */ private final Map<Pair<String, Integer>, InterfaceCacheEntry> mInterfaceInfoCache = new HashMap<>(); + private WifiChipInfo[] mDebugChipsInfo = null; private class InterfaceCacheEntry { public IWifiChip chip; @@ -1125,6 +1149,7 @@ public class HalDeviceManager { chipInfo.ifaces[IfaceType.NAN] = nanIfaces; } + if (mDebugChipsInfo == null) mDebugChipsInfo = chipsInfo; return chipsInfo; } catch (RemoteException e) { Log.e(TAG, "getAllChipInfoAndValidateCache exception: " + e); @@ -1771,6 +1796,53 @@ public class HalDeviceManager { } /** + * Checks whether the input chip-iface-combo can support the requested interface type. + */ + private boolean canIfaceComboSupportRequestedIfaceCombo( + int[] chipIfaceCombo, int[] requestedIfaceCombo) { + if (VDBG) { + Log.d(TAG, "canIfaceComboSupportRequest: chipIfaceCombo=" + chipIfaceCombo + + ", requestedIfaceCombo=" + requestedIfaceCombo); + } + for (int ifaceType : IFACE_TYPES_BY_PRIORITY) { + if (chipIfaceCombo[ifaceType] < requestedIfaceCombo[ifaceType]) { + if (VDBG) Log.d(TAG, "Requested type not supported by combo"); + return false; + } + } + return true; + } + + // Is it possible to create iface combo just looking at the device capabilities. + private boolean isItPossibleToCreateIfaceCombo(WifiChipInfo[] chipInfos, int[] ifaceCombo) { + if (VDBG) { + Log.d(TAG, "isItPossibleToCreateIfaceCombo: chipInfos=" + Arrays.deepToString(chipInfos) + + ", ifaceType=" + ifaceCombo); + } + + for (WifiChipInfo chipInfo: chipInfos) { + for (IWifiChip.ChipMode chipMode: chipInfo.availableModes) { + for (IWifiChip.ChipIfaceCombination chipIfaceCombo + : chipMode.availableCombinations) { + int[][] expandedIfaceCombos = expandIfaceCombos(chipIfaceCombo); + if (VDBG) { + Log.d(TAG, chipIfaceCombo + " expands to " + + Arrays.deepToString(expandedIfaceCombos)); + } + + for (int[] expandedIfaceCombo: expandedIfaceCombos) { + if (canIfaceComboSupportRequestedIfaceCombo( + expandedIfaceCombo, ifaceCombo)) { + return true; + } + } + } + } + } + return false; + } + + /** * Performs chip reconfiguration per the input: * - Removes the specified interfaces * - Reconfigures the chip to the new chip mode (if necessary) @@ -1953,6 +2025,7 @@ public class HalDeviceManager { return true; } + private void dispatchAvailableForRequestListenersForType(int ifaceType, WifiChipInfo[] chipInfos) { if (VDBG) Log.d(TAG, "dispatchAvailableForRequestListenersForType: ifaceType=" + ifaceType); @@ -2277,5 +2350,6 @@ public class HalDeviceManager { pw.println(" mInterfaceAvailableForRequestListeners: " + mInterfaceAvailableForRequestListeners); pw.println(" mInterfaceInfoCache: " + mInterfaceInfoCache); + pw.println(" mDebugChipsInfo: " + Arrays.toString(mDebugChipsInfo)); } } diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java index e0e185b71..fa1295dd2 100644 --- a/service/java/com/android/server/wifi/WifiNative.java +++ b/service/java/com/android/server/wifi/WifiNative.java @@ -2846,6 +2846,15 @@ public class WifiNative { } /** + * Returns whether STA/AP concurrency is supported or not. + */ + public boolean isStaApConcurrencySupported() { + synchronized (mLock) { + return mWifiVendorHal.isStaApConcurrencySupported(); + } + } + + /** * Get the supported features * * @param ifaceName Name of the interface. diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java index 91f776834..a373c3fae 100644 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -3705,7 +3705,7 @@ public class WifiServiceImpl extends BaseWifiService { supportedFeatureSet |= WifiManager.WIFI_FEATURE_AP_RAND_MAC; } if (mWifiThreadRunner.call( - () -> mActiveModeWarden.canSupportAtleastOneConcurrentClientAndSoftApManager(), + () -> mActiveModeWarden.isStaApConcurrencySupported(), false)) { supportedFeatureSet |= WifiManager.WIFI_FEATURE_AP_STA; } diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java index cc9b9ac8e..f01f9a94c 100644 --- a/service/java/com/android/server/wifi/WifiVendorHal.java +++ b/service/java/com/android/server/wifi/WifiVendorHal.java @@ -59,6 +59,7 @@ import android.text.TextUtils; import android.util.Log; import android.util.MutableBoolean; import android.util.MutableLong; +import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.HexDump; @@ -2635,6 +2636,18 @@ public class WifiVendorHal { } } + /** + * Returns whether STA/AP concurrency is supported or not. + */ + public boolean isStaApConcurrencySupported() { + synchronized (sLock) { + return mHalDeviceManager.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.STA, 1); + put(IfaceType.AP, 1); + }}); + } + } + // This creates a blob of IE elements from the array received. // TODO: This ugly conversion can be removed if we put IE elements in ScanResult. private static byte[] hidlIeArrayToFrameworkIeBlob(ArrayList<WifiInformationElement> ies) { diff --git a/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java b/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java index c68cd9ac5..701ffb345 100644 --- a/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java +++ b/tests/wifitests/src/com/android/server/wifi/ActiveModeWardenTest.java @@ -2151,41 +2151,11 @@ public class ActiveModeWardenTest extends WifiBaseTest { } @Test - public void canSupportAtleastOneConcurrentClientAndSoftApManager() throws Exception { - assertNotNull(mClientIfaceAvailableListener.getValue()); - assertNotNull(mSoftApIfaceAvailableListener.getValue()); - - // No mode manager - assertFalse(mActiveModeWarden.canSupportAtleastOneConcurrentClientAndSoftApManager()); - - // client mode manager active, but cannot create one more softap manager - enterClientModeActiveState(); - assertFalse(mActiveModeWarden.canSupportAtleastOneConcurrentClientAndSoftApManager()); - - // client mode manager active, can create one more softap manager - mSoftApIfaceAvailableListener.getValue().onAvailabilityChanged(true); - mLooper.dispatchAll(); - assertTrue(mActiveModeWarden.canSupportAtleastOneConcurrentClientAndSoftApManager()); - - // Tear down client mode manager - enterStaDisabledMode(false); + public void isStaApConcurrencySupported() throws Exception { + when(mWifiNative.isStaApConcurrencySupported()).thenReturn(false); + assertFalse(mActiveModeWarden.isStaApConcurrencySupported()); - // active softap manager, but cannot create one more client mode manager - reset(mSoftApManager, mBatteryStats); - enterSoftApActiveMode(); - assertFalse(mActiveModeWarden.canSupportAtleastOneConcurrentClientAndSoftApManager()); - - // active softap manager, can create one more client mode manager - mClientIfaceAvailableListener.getValue().onAvailabilityChanged(true); - mLooper.dispatchAll(); - assertTrue(mActiveModeWarden.canSupportAtleastOneConcurrentClientAndSoftApManager()); - - // softap manager + client mode manager active, cannot create any more mode managers - reset(mClientModeManager, mBatteryStats, mScanRequestProxy); - enterClientModeActiveState(); - mSoftApIfaceAvailableListener.getValue().onAvailabilityChanged(false); - mClientIfaceAvailableListener.getValue().onAvailabilityChanged(false); - mLooper.dispatchAll(); - assertTrue(mActiveModeWarden.canSupportAtleastOneConcurrentClientAndSoftApManager()); + when(mWifiNative.isStaApConcurrencySupported()).thenReturn(true); + assertTrue(mActiveModeWarden.isStaApConcurrencySupported()); } } diff --git a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java index 36bf52b6b..1b195cfc7 100644 --- a/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/HalDeviceManagerTest.java @@ -56,6 +56,7 @@ import android.os.Handler; import android.os.IHwBinder; import android.os.test.TestLooper; import android.util.Log; +import android.util.SparseArray; import androidx.test.filters.SmallTest; @@ -1361,6 +1362,66 @@ public class HalDeviceManagerTest extends WifiBaseTest { assertEquals(correctResults, results); } + /** + * Validate {@link HalDeviceManager#canSupportIfaceCombo(SparseArray)} + */ + @Test + public void testCanSupportIfaceComboTestChipV1() throws Exception { + final String name = "wlan0"; + + TestChipV1 chipMock = new TestChipV1(); + chipMock.initialize(); + mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip, + mManagerStatusListenerMock); + executeAndValidateInitializationSequence(); + executeAndValidateStartupSequence(); + + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.STA, 1); + }} + )); + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.AP, 1); + }} + )); + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.P2P, 1); + }} + )); + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.NAN, 1); + }} + )); + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.STA, 1); + put(IfaceType.P2P, 1); + }} + )); + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.STA, 1); + put(IfaceType.NAN, 1); + }} + )); + + assertFalse(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.STA, 1); + put(IfaceType.AP, 1); + }} + )); + assertFalse(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.STA, 2); + }} + )); + assertFalse(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.P2P, 1); + put(IfaceType.NAN, 1); + }} + )); + + verifyNoMoreInteractions(mManagerStatusListenerMock); + } + + ////////////////////////////////////////////////////////////////////////////////////// // TestChipV2 Specific Tests ////////////////////////////////////////////////////////////////////////////////////// @@ -1632,6 +1693,91 @@ public class HalDeviceManagerTest extends WifiBaseTest { assertEquals(correctResults, results); } + /** + * Validate {@link HalDeviceManager#canSupportIfaceCombo(SparseArray)} + */ + @Test + public void testCanSupportIfaceComboTestChipV2() throws Exception { + final String name = "wlan0"; + + TestChipV2 chipMock = new TestChipV2(); + chipMock.initialize(); + mInOrder = inOrder(mServiceManagerMock, mWifiMock, chipMock.chip, + mManagerStatusListenerMock); + executeAndValidateInitializationSequence(); + executeAndValidateStartupSequence(); + + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.STA, 1); + }} + )); + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.AP, 1); + }} + )); + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.P2P, 1); + }} + )); + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.NAN, 1); + }} + )); + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.STA, 1); + put(IfaceType.P2P, 1); + }} + )); + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.STA, 1); + put(IfaceType.NAN, 1); + }} + )); + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.STA, 1); + put(IfaceType.AP, 1); + }} + )); + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.STA, 2); + }} + )); + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.P2P, 1); + put(IfaceType.AP, 1); + }} + )); + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.STA, 1); + put(IfaceType.P2P, 1); + put(IfaceType.AP, 1); + }} + )); + assertTrue(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.STA, 1); + put(IfaceType.AP, 1); + put(IfaceType.NAN, 1); + }} + )); + + assertFalse(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.P2P, 1); + put(IfaceType.NAN, 1); + }} + )); + assertFalse(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.AP, 2); + }} + )); + assertFalse(mDut.canSupportIfaceCombo(new SparseArray<Integer>() {{ + put(IfaceType.STA, 2); + put(IfaceType.AP, 1); + }} + )); + + verifyNoMoreInteractions(mManagerStatusListenerMock); + } + ////////////////////////////////////////////////////////////////////////////////////// // TestChipV3 Specific Tests ////////////////////////////////////////////////////////////////////////////////////// diff --git a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java index 3a5ce5272..50fd4d8c1 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiApConfigStoreTest.java @@ -112,7 +112,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { mResources.setString(R.string.wifi_localhotspot_configure_ssid_default, TEST_DEFAULT_HOTSPOT_SSID); /* Default to device that does not require ap band conversion */ - when(mActiveModeWarden.canSupportAtleastOneConcurrentClientAndSoftApManager()) + when(mActiveModeWarden.isStaApConcurrencySupported()) .thenReturn(false); when(mContext.getResources()).thenReturn(mResources); @@ -282,7 +282,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { */ @Test public void convertDevice5GhzToAny() throws Exception { - when(mActiveModeWarden.canSupportAtleastOneConcurrentClientAndSoftApManager()) + when(mActiveModeWarden.isStaApConcurrencySupported()) .thenReturn(true); /* Initialize WifiApConfigStore with default configuration. */ @@ -321,7 +321,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { */ @Test public void deviceAnyNotConverted() throws Exception { - when(mActiveModeWarden.canSupportAtleastOneConcurrentClientAndSoftApManager()) + when(mActiveModeWarden.isStaApConcurrencySupported()) .thenReturn(true); /* Initialize WifiApConfigStore with default configuration. */ @@ -350,7 +350,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { */ @Test public void deviceWithChannelNotConverted() throws Exception { - when(mActiveModeWarden.canSupportAtleastOneConcurrentClientAndSoftApManager()) + when(mActiveModeWarden.isStaApConcurrencySupported()) .thenReturn(true); /* Initialize WifiApConfigStore with default configuration. */ @@ -380,7 +380,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { */ @Test public void device5GhzConvertedToAnyAtRetrieval() throws Exception { - when(mActiveModeWarden.canSupportAtleastOneConcurrentClientAndSoftApManager()) + when(mActiveModeWarden.isStaApConcurrencySupported()) .thenReturn(true); SoftApConfiguration persistedConfig = setupApConfig( @@ -414,7 +414,7 @@ public class WifiApConfigStoreTest extends WifiBaseTest { */ @Test public void deviceNotConvertedAtRetrieval() throws Exception { - when(mActiveModeWarden.canSupportAtleastOneConcurrentClientAndSoftApManager()) + when(mActiveModeWarden.isStaApConcurrencySupported()) .thenReturn(true); SoftApConfiguration persistedConfig = setupApConfig( diff --git a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java index ca8c9dbc6..b5c5e7b42 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiServiceImplTest.java @@ -5632,14 +5632,14 @@ public class WifiServiceImplTest extends WifiBaseTest { when(mClientModeImpl.syncGetSupportedFeatures( any())).thenReturn(supportedFeaturesFromClientModeImpl); - when(mActiveModeWarden.canSupportAtleastOneConcurrentClientAndSoftApManager()) + when(mActiveModeWarden.isStaApConcurrencySupported()) .thenReturn(false); mLooper.startAutoDispatch(); assertEquals(supportedFeaturesFromClientModeImpl, mWifiServiceImpl.getSupportedFeatures()); mLooper.stopAutoDispatchAndIgnoreExceptions(); - when(mActiveModeWarden.canSupportAtleastOneConcurrentClientAndSoftApManager()) + when(mActiveModeWarden.isStaApConcurrencySupported()) .thenReturn(true); mLooper.startAutoDispatch(); assertEquals(supportedFeaturesFromClientModeImpl | WifiManager.WIFI_FEATURE_AP_STA, |