From ddfe48103565da1edfa1a1bece5d2fd5f2e5b064 Mon Sep 17 00:00:00 2001 From: Sunil Ravi Date: Thu, 20 Feb 2020 09:56:25 -0800 Subject: DPP R2 compatibility check On receiving DPP error code "CANNOT_FIND_NETWORK", look into the scan cache and update the error code to "NOT_COMPATIBLE", if network's operating channel is not present in enrollee's scanned channel. Bug: 149477842 Test: atest Test: Manual, DPP connection success/failure tests. Change-Id: I786e7541ea365836189979053270cd6748d6adc4 (cherry picked from commit d7e35020761ebdababbf4da43145a5459b296a54) --- .../java/com/android/server/wifi/DppManager.java | 102 ++++++++++++++++++++- .../java/com/android/server/wifi/WifiInjector.java | 2 +- .../com/android/server/wifi/util/ApConfigUtil.java | 34 +++++++ .../com/android/server/wifi/DppManagerTest.java | 64 ++++++++++++- 4 files changed, 195 insertions(+), 7 deletions(-) diff --git a/service/java/com/android/server/wifi/DppManager.java b/service/java/com/android/server/wifi/DppManager.java index 1f228bcb5..11572f1d2 100644 --- a/service/java/com/android/server/wifi/DppManager.java +++ b/service/java/com/android/server/wifi/DppManager.java @@ -26,17 +26,22 @@ import android.hardware.wifi.supplicant.V1_3.DppProgressCode; import android.hardware.wifi.supplicant.V1_3.DppSuccessCode; import android.net.wifi.EasyConnectStatusCallback; import android.net.wifi.IDppCallback; +import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.WakeupMessage; import com.android.server.wifi.WifiNative.DppEventCallback; +import com.android.server.wifi.util.ApConfigUtil; +import java.util.ArrayList; +import java.util.List; /** * DPP Manager class * Implements the DPP Initiator APIs and callbacks @@ -57,6 +62,7 @@ public class DppManager { private static final String DPP_TIMEOUT_TAG = TAG + " Request Timeout"; private static final int DPP_TIMEOUT_MS = 40_000; // 40 seconds private final DppMetrics mDppMetrics; + private final ScanRequestProxy mScanRequestProxy; private final DppEventCallback mDppEventCallback = new DppEventCallback() { @Override @@ -89,7 +95,7 @@ public class DppManager { }; DppManager(Handler handler, WifiNative wifiNative, WifiConfigManager wifiConfigManager, - Context context, DppMetrics dppMetrics) { + Context context, DppMetrics dppMetrics, ScanRequestProxy scanRequestProxy) { mHandler = handler; mWifiNative = wifiNative; mWifiConfigManager = wifiConfigManager; @@ -97,6 +103,7 @@ public class DppManager { mContext = context; mClock = new Clock(); mDppMetrics = dppMetrics; + mScanRequestProxy = scanRequestProxy; // Setup timer mDppTimeoutMessage = new WakeupMessage(mContext, mHandler, @@ -558,6 +565,88 @@ public class DppManager { onFailure(dppStatusCode, null, null, null); } + /** + * + * This function performs the Enrollee compatibility check with the network. + * Compatibilty check is done based on the channel match. + * The logic looks into the scan cache and checks if network's + * operating channel match with one of the channel in enrollee's scanned channel list. + * + * @param ssid Network name. + * @param channelList contains the list of operating class/channels enrollee used to search for + * the network. + * Reference: DPP spec section: DPP Connection Status Object section. + * (eg for channelList: "81/1,2,3,4,5,6,7,8,9,10,11,117/40,115/48") + * @return True On compatibility check failures due to error conditions or + * when AP is not seen in scan cache or when AP is seen in scan cache and + * operating channel is included in enrollee's scanned channel list. + * False when network's operating channel is not included in Enrollee's + * scanned channel list. + * + */ + private boolean isEnrolleeCompatibleWithNetwork(String ssid, String channelList) { + if (ssid == null || channelList == null) { + return true; + } + SparseArray dppChannelList = WifiManager.parseDppChannelList(channelList); + + if (dppChannelList.size() == 0) { + Log.d(TAG, "No channels found after parsing channel list string"); + return true; + } + + List freqList = new ArrayList(); + + /* Convert the received operatingClass/channels to list of frequencies */ + for (int i = 0; i < dppChannelList.size(); i++) { + /* Derive the band corresponding to operating class */ + int operatingClass = dppChannelList.keyAt(i); + int[] channels = dppChannelList.get(operatingClass); + int band = ApConfigUtil.getBandFromOperatingClass(operatingClass); + if (band < 0) { + Log.e(TAG, "Band corresponding to the operating class: " + operatingClass + + " not found in the table"); + continue; + } + /* Derive frequency list from channel and band */ + for (int j = 0; j < channels.length; j++) { + int freq = ApConfigUtil.convertChannelToFrequency(channels[j], band); + if (freq < 0) { + Log.e(TAG, "Invalid frequency after converting channel: " + channels[j] + + " band: " + band); + continue; + } + freqList.add(freq); + } + } + + if (freqList.size() == 0) { + Log.d(TAG, "frequency list is empty"); + return true; + } + + /* Check the scan cache for the network enrollee tried to find */ + boolean isNetworkInScanCache = false; + boolean channelMatch = false; + for (ScanResult scanResult : mScanRequestProxy.getScanResults()) { + if (!ssid.equals(scanResult.SSID)) { + continue; + } + isNetworkInScanCache = true; + if (freqList.contains(scanResult.frequency)) { + channelMatch = true; + break; + } + } + + if (isNetworkInScanCache & !channelMatch) { + Log.d(TAG, "Update the error code to NOT_COMPATIBLE" + + " as enrollee didn't scan network's operating channel"); + return false; + } + return true; + } + private void onFailure(int dppStatusCode, String ssid, String channelList, int[] bandList) { try { if (mDppRequestInfo == null) { @@ -610,8 +699,15 @@ public class DppManager { case DppFailureCode.CANNOT_FIND_NETWORK: // This is the only case where channel list is populated, according to the // DPP spec section 6.3.5.2 DPP Connection Status Object - dppFailureCode = EasyConnectStatusCallback - .EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK; + if (isEnrolleeCompatibleWithNetwork(ssid, channelList)) { + dppFailureCode = + EasyConnectStatusCallback + .EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK; + } else { + dppFailureCode = + EasyConnectStatusCallback + .EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE; + } break; case DppFailureCode.ENROLLEE_AUTHENTICATION: diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java index 71f8b7073..787f89331 100644 --- a/service/java/com/android/server/wifi/WifiInjector.java +++ b/service/java/com/android/server/wifi/WifiInjector.java @@ -357,7 +357,7 @@ public class WifiInjector { mWifiMulticastLockManager = new WifiMulticastLockManager( mClientModeImpl.getMcastLockManagerFilterController(), mBatteryStats); mDppManager = new DppManager(wifiHandler, mWifiNative, - mWifiConfigManager, mContext, mDppMetrics); + mWifiConfigManager, mContext, mDppMetrics, mScanRequestProxy); // Register the various network Nominators with the network selector. mWifiNetworkSelector.registerNetworkNominator(mSavedNetworkNominator); diff --git a/service/java/com/android/server/wifi/util/ApConfigUtil.java b/service/java/com/android/server/wifi/util/ApConfigUtil.java index bf319b4f2..7703f2819 100644 --- a/service/java/com/android/server/wifi/util/ApConfigUtil.java +++ b/service/java/com/android/server/wifi/util/ApConfigUtil.java @@ -27,6 +27,7 @@ import android.net.wifi.SoftApConfiguration.BandType; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiScanner; import android.util.Log; +import android.util.SparseArray; import com.android.server.wifi.WifiNative; import com.android.wifi.resources.R; @@ -55,6 +56,39 @@ public class ApConfigUtil { /* Random number generator used for AP channel selection. */ private static final Random sRandom = new Random(); + /** + * Valid Global Operating classes in each wifi band + * Reference: Table E-4 in IEEE Std 802.11-2016. + */ + private static final SparseArray sBandToOperatingClass = new SparseArray<>(); + static { + sBandToOperatingClass.append(SoftApConfiguration.BAND_2GHZ, new int[]{81, 82, 83, 84}); + sBandToOperatingClass.append(SoftApConfiguration.BAND_5GHZ, new int[]{115, 116, 117, 118, + 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130}); + sBandToOperatingClass.append(SoftApConfiguration.BAND_6GHZ, new int[]{131, 132, 133, 134}); + } + + /** + * Helper function to get the band corresponding to the operating class. + * + * @param operatingClass Global operating class. + * @return band, -1 if no match. + * + */ + public static int getBandFromOperatingClass(int operatingClass) { + for (int i = 0; i < sBandToOperatingClass.size(); i++) { + int band = sBandToOperatingClass.keyAt(i); + int[] operatingClasses = sBandToOperatingClass.get(band); + + for (int j = 0; j < operatingClasses.length; j++) { + if (operatingClasses[j] == operatingClass) { + return band; + } + } + } + return -1; + } + /** * Convert channel/band to frequency. * Note: the utility does not perform any regulatory domain compliance. diff --git a/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java b/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java index 9bfa9b6a2..19d17c979 100644 --- a/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/DppManagerTest.java @@ -66,7 +66,9 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.net.wifi.EasyConnectStatusCallback; import android.net.wifi.IDppCallback; +import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiSsid; import android.os.Handler; import android.os.IBinder; import android.os.test.TestLooper; @@ -80,6 +82,9 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.List; + /** * Unit tests for {@link DppManager}. */ @@ -89,10 +94,12 @@ public class DppManagerTest extends WifiBaseTest { private static final String TEST_INTERFACE_NAME = "testif0"; private static final int TEST_PEER_ID = 1; private static final String TEST_SSID = "\"Test_SSID\""; + private static final String TEST_SSID_NO_QUOTE = TEST_SSID.replace("\"", ""); private static final String TEST_SSID_ENCODED = "546573745f53534944"; private static final String TEST_PASSWORD = "\"secretPassword\""; private static final String TEST_PASSWORD_ENCODED = "73656372657450617373776f7264"; private static final int TEST_NETWORK_ID = 1; + private static final String TEST_BSSID = "01:02:03:04:05:06"; TestLooper mLooper; @@ -116,6 +123,8 @@ public class DppManagerTest extends WifiBaseTest { WakeupMessage mWakeupMessage; @Mock DppMetrics mDppMetrics; + @Mock + ScanRequestProxy mScanRequestProxy; String mUri = "DPP:C:81/1;I:DPP_TESTER;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADebGHMJoCcE7OZP/aek5muaJo" @@ -148,7 +157,7 @@ public class DppManagerTest extends WifiBaseTest { private DppManager createDppManager() { DppManager dppManger = new DppManager(new Handler(mLooper.getLooper()), mWifiNative, - mWifiConfigManager, mContext, mDppMetrics); + mWifiConfigManager, mContext, mDppMetrics, mScanRequestProxy); dppManger.mDppTimeoutMessage = mWakeupMessage; dppManger.enableVerboseLogging(1); return dppManger; @@ -741,10 +750,59 @@ public class DppManagerTest extends WifiBaseTest { testOnFailureCallback(FAILURE, EASY_CONNECT_EVENT_FAILURE_GENERIC); } + /** + * Helper function for setting up a scan result. + * + * @param frequency Network's operating channel in Mhz. + * + */ + private void addTestNetworkInScanResult(int frequency) { + String caps = "[WPA2-FT/SAE+SAE][ESS][WPS]"; + ScanResult scanResult = new ScanResult(WifiSsid.createFromAsciiEncoded(TEST_SSID_NO_QUOTE), + TEST_SSID_NO_QUOTE, TEST_BSSID, 1245, 0, caps, -78, frequency, + 1025, 22, 33, 20, 0, 0, true); + List scanResults = new ArrayList<>(); + scanResults.add(scanResult); + when(mScanRequestProxy.getScanResults()).thenReturn(scanResults); + } + + @Test + public void testOnFailureCallbackCannotFindNetworkNoErrCodeChangeOnChannelMatch() + throws Exception { + int[] bandList = new int[]{81, 83, 84, 115}; + addTestNetworkInScanResult(2437); //channel number 6 + // Include Network channel(6) in Enrollee scanned channels. + testOnFailureCallback(CANNOT_FIND_NETWORK, TEST_SSID_NO_QUOTE, + "81/1,2,3,4,5,6,7,8,9,10,11,115/48", + bandList, EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK); + } + + @Test + public void testOnFailureCallbackCannotFindNetworkErrCodeIsUpdatedOnChannelMismatch() + throws Exception { + int[] bandList = new int[]{81, 83, 84, 115}; + addTestNetworkInScanResult(5180); //channel number 36 + // Don't include Network channel(36) in Enrollee scanned channels. + testOnFailureCallback(CANNOT_FIND_NETWORK, TEST_SSID_NO_QUOTE, + "81/1,2,3,4,5,6,7,8,9,10,11,115/48", + bandList, EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE); + } + + @Test + public void testOnFailureCallbackCannotFindNetworkNoErrCodeChangeOnInvalidScannedChannelList() + throws Exception { + int[] bandList = new int[]{81, 83, 84}; + addTestNetworkInScanResult(5180); //channel number 36 + // Send Invalid operating class/channel list + testOnFailureCallback(CANNOT_FIND_NETWORK, TEST_SSID_NO_QUOTE, + "1035/1,2,3,4,5,6,7,8,9,10,11,25/45", + bandList, EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK); + } + @Test public void testOnFailureCallbackCannotFindNetwork() throws Exception { int[] bandList = new int[]{1, 6, 11}; - testOnFailureCallback(CANNOT_FIND_NETWORK, "SSID", "1, 3, 6, 8, 11", + testOnFailureCallback(CANNOT_FIND_NETWORK, TEST_SSID_NO_QUOTE, "1, 3, 6, 8, 11", bandList, EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK); } @@ -757,7 +815,7 @@ public class DppManagerTest extends WifiBaseTest { @Test public void testOnFailureCallbackEnrolleeAuthentication() throws Exception { int[] bandList = new int[]{1, 6, 11}; - testOnFailureCallback(ENROLLEE_AUTHENTICATION, new String("SSID"), null, + testOnFailureCallback(ENROLLEE_AUTHENTICATION, new String(TEST_SSID_NO_QUOTE), null, bandList, EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION); } -- cgit v1.2.3