Wifi: Country code during Wifi scan only mode

When entering the scan only mode state country
code of the area should be applied even if Supplicant
is not running.

Bug: 149936939
Test: atest com.android.server.wifi
Change-Id: I53716699e90942d089aea09a7201e46c554dcea1
diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java
index 132e6c0..71a6dfd 100644
--- a/service/java/com/android/server/wifi/ClientModeImpl.java
+++ b/service/java/com/android/server/wifi/ClientModeImpl.java
@@ -5986,7 +5986,7 @@
 
     @Override
     public boolean setCountryCode(String countryCode) {
-        return mWifiNative.setCountryCode(mInterfaceName, countryCode);
+        return mWifiNative.setStaCountryCode(mInterfaceName, countryCode);
     }
 
     @Override
diff --git a/service/java/com/android/server/wifi/ConcreteClientModeManager.java b/service/java/com/android/server/wifi/ConcreteClientModeManager.java
index 89b8278..4446a4d 100644
--- a/service/java/com/android/server/wifi/ConcreteClientModeManager.java
+++ b/service/java/com/android/server/wifi/ConcreteClientModeManager.java
@@ -119,6 +119,7 @@
     private final DefaultClientModeManager mDefaultClientModeManager;
     private final long mId;
     private final Graveyard mGraveyard = new Graveyard();
+    private final WifiCountryCode mCountryCode;
 
     private String mClientInterfaceName;
     private boolean mIfaceIsUp = false;
@@ -160,7 +161,8 @@
             SelfRecovery selfRecovery, WifiGlobals wifiGlobals,
             DefaultClientModeManager defaultClientModeManager, long id,
             @NonNull WorkSource requestorWs, @NonNull ClientRole role,
-            boolean verboseLoggingEnabled) {
+            boolean verboseLoggingEnabled,
+            @NonNull WifiCountryCode countryCode) {
         mContext = context;
         mClock = clock;
         mWifiNative = wifiNative;
@@ -177,6 +179,7 @@
         mTargetRole = role;
         mTargetRequestorWs = requestorWs;
         enableVerboseLogging(verboseLoggingEnabled);
+        mCountryCode = countryCode;
         mStateMachine.sendMessage(ClientModeStateMachine.CMD_START, Pair.create(role, requestorWs));
     }
 
@@ -835,6 +838,7 @@
                 }
 
                 setRoleInternalAndInvokeCallback(ROLE_CLIENT_SCAN_ONLY);
+                mCountryCode.setReadyForChange(true);
                 mWakeupController.start();
             }
 
diff --git a/service/java/com/android/server/wifi/ScanOnlyModeImpl.java b/service/java/com/android/server/wifi/ScanOnlyModeImpl.java
index 9f2babf..6449b6a 100644
--- a/service/java/com/android/server/wifi/ScanOnlyModeImpl.java
+++ b/service/java/com/android/server/wifi/ScanOnlyModeImpl.java
@@ -50,6 +50,11 @@
     }
 
     @Override
+    public boolean setCountryCode(String countryCode) {
+        return mWifiNative.setChipCountryCode(countryCode);
+    }
+
+    @Override
     public String toString() {
         return "ScanOnlyModeImpl{"
                 + "id=" + mId
diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java
index 03d261d..4af118b 100644
--- a/service/java/com/android/server/wifi/SoftApManager.java
+++ b/service/java/com/android/server/wifi/SoftApManager.java
@@ -442,7 +442,7 @@
             return SUCCESS;
         }
 
-        if (!mWifiNative.setCountryCodeHal(
+        if (!mWifiNative.setApCountryCode(
                 mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT))) {
             if (band == SoftApConfiguration.BAND_5GHZ) {
                 // Return an error if failed to set country code when AP is configured for
diff --git a/service/java/com/android/server/wifi/WifiCountryCode.java b/service/java/com/android/server/wifi/WifiCountryCode.java
index bbcdc87..93e0a1e 100644
--- a/service/java/com/android/server/wifi/WifiCountryCode.java
+++ b/service/java/com/android/server/wifi/WifiCountryCode.java
@@ -249,7 +249,7 @@
         // We do not check if the country code equals the current one.
         // There are two reasons:
         // 1. Wpa supplicant may silently modify the country code.
-        // 2. If Wifi restarted therefoere wpa_supplicant also restarted,
+        // 2. If Wifi restarted therefore wpa_supplicant also restarted,
         // the country code counld be reset to '00' by wpa_supplicant.
         if (country != null) {
             setCountryCodeNative(country);
@@ -274,14 +274,21 @@
     }
 
     private boolean setCountryCodeNative(String country) {
-        mDriverCountryTimestamp = FORMATTER.format(new Date(System.currentTimeMillis()));
-        if (mActiveModeWarden.getPrimaryClientModeManager().setCountryCode(country)) {
-            Log.d(TAG, "Succeeded to set country code to: " + country);
-            mDriverCountryCode = country;
-            for (ChangeListener listener : mListeners) {
-                listener.onDriverCountryCodeChanged(country);
+        List<ClientModeManager> cmms = mActiveModeWarden.getClientModeManagers();
+
+        // Set the country code using one of the active client mode managers. Since
+        // country code is a chip level global setting, it can be set as long
+        // as there is at least one active interface to communicate to Wifi chip
+        for (ClientModeManager cm : cmms) {
+            if (cm.setCountryCode(country)) {
+                mDriverCountryTimestamp = FORMATTER.format(new Date(System.currentTimeMillis()));
+                mDriverCountryCode = country;
+                Log.d(TAG, "Succeeded to set country code to: " + country);
+                for (ChangeListener listener : mListeners) {
+                    listener.onDriverCountryCodeChanged(country);
+                }
+                return true;
             }
-            return true;
         }
         Log.d(TAG, "Failed to set country code to: " + country);
         return false;
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 1703d73..5819cc3 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -709,7 +709,8 @@
                 mContext, mWifiHandlerThread.getLooper(), mClock,
                 mWifiNative, listener, mWifiMetrics, mWakeupController,
                 this, mSelfRecovery, mWifiGlobals, mDefaultClientModeManager,
-                mClock.getElapsedSinceBootMillis(), requestorWs, role, verboseLoggingEnabled);
+                mClock.getElapsedSinceBootMillis(), requestorWs, role, verboseLoggingEnabled,
+                mCountryCode);
     }
 
     public ScanOnlyModeImpl makeScanOnlyModeImpl(@NonNull String ifaceName) {
diff --git a/service/java/com/android/server/wifi/WifiNative.java b/service/java/com/android/server/wifi/WifiNative.java
index 89936fc..f9cb365 100644
--- a/service/java/com/android/server/wifi/WifiNative.java
+++ b/service/java/com/android/server/wifi/WifiNative.java
@@ -2131,13 +2131,13 @@
     }
 
     /**
-     * Set country code.
+     * Set country code for STA interface
      *
-     * @param ifaceName Name of the interface.
+     * @param ifaceName Name of the STA interface.
      * @param countryCode 2 byte ASCII string. For ex: US, CA.
      * @return true if request is sent successfully, false otherwise.
      */
-    public boolean setCountryCode(@NonNull String ifaceName, String countryCode) {
+    public boolean setStaCountryCode(@NonNull String ifaceName, String countryCode) {
         return mSupplicantStaIfaceHal.setCountryCode(ifaceName, countryCode);
     }
 
@@ -3236,12 +3236,21 @@
 
     /**
      * Set country code for this AP iface.
-     * @param ifaceName Name of the interface.
+     * @param ifaceName Name of the AP interface.
      * @param countryCode - two-letter country code (as ISO 3166)
      * @return true for success
      */
-    public boolean setCountryCodeHal(@NonNull String ifaceName, String countryCode) {
-        return mWifiVendorHal.setCountryCodeHal(ifaceName, countryCode);
+    public boolean setApCountryCode(@NonNull String ifaceName, String countryCode) {
+        return mWifiVendorHal.setApCountryCode(ifaceName, countryCode);
+    }
+
+    /**
+     * Set country code for this chip
+     * @param countryCode - two-letter country code (as ISO 3166)
+     * @return true for success
+     */
+    public boolean setChipCountryCode(String countryCode) {
+        return mWifiVendorHal.setChipCountryCode(countryCode);
     }
 
     //---------------------------------------------------------------------------------
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java
index 084debf..dcae040 100644
--- a/service/java/com/android/server/wifi/WifiServiceImpl.java
+++ b/service/java/com/android/server/wifi/WifiServiceImpl.java
@@ -1198,7 +1198,7 @@
     private final class CountryCodeListenerProxy implements WifiCountryCode.ChangeListener {
         @Override
         public void onDriverCountryCodeChanged(String countryCode) {
-            Log.i(TAG, "onDriverCountryCodeChanged" + countryCode);
+            Log.i(TAG, "onDriverCountryCodeChanged " + countryCode);
             mTetheredSoftApTracker.updateAvailChannelListInSoftApCapability();
             mActiveModeWarden.updateSoftApCapability(
                     mTetheredSoftApTracker.getSoftApCapability());
diff --git a/service/java/com/android/server/wifi/WifiVendorHal.java b/service/java/com/android/server/wifi/WifiVendorHal.java
index 489f512..8f7f816 100644
--- a/service/java/com/android/server/wifi/WifiVendorHal.java
+++ b/service/java/com/android/server/wifi/WifiVendorHal.java
@@ -1743,13 +1743,42 @@
     }
 
     /**
+     * Set country code for this Wifi chip
+     *
+     * @param countryCode - two-letter country code (as ISO 3166)
+     * @return true for success
+     */
+    public boolean setChipCountryCode(String countryCode) {
+        if (countryCode == null) return boolResult(false);
+        if (countryCode.length() != 2) return boolResult(false);
+        byte[] code;
+        try {
+            code = NativeUtil.stringToByteArray(countryCode);
+        } catch (IllegalArgumentException e) {
+            return boolResult(false);
+        }
+        synchronized (sLock) {
+            try {
+                android.hardware.wifi.V1_5.IWifiChip iWifiChipV15 = getWifiChipForV1_5Mockable();
+                if (iWifiChipV15 == null) return boolResult(false);
+                WifiStatus status = iWifiChipV15.setCountryCode(code);
+                if (!ok(status)) return false;
+                return true;
+            } catch (RemoteException e) {
+                handleRemoteException(e);
+                return false;
+            }
+        }
+    }
+
+    /**
      * Set country code for this AP iface.
      *
      * @param ifaceName Name of the interface.
      * @param countryCode - two-letter country code (as ISO 3166)
      * @return true for success
      */
-    public boolean setCountryCodeHal(@NonNull String ifaceName, String countryCode) {
+    public boolean setApCountryCode(@NonNull String ifaceName, String countryCode) {
         if (countryCode == null) return boolResult(false);
         if (countryCode.length() != 2) return boolResult(false);
         byte[] code;
diff --git a/service/tests/wifitests/src/com/android/server/wifi/ConcreteClientModeManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/ConcreteClientModeManagerTest.java
index 3f8b777..a34ae71 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/ConcreteClientModeManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/ConcreteClientModeManagerTest.java
@@ -113,6 +113,8 @@
     @Mock WifiGlobals mWifiGlobals;
     @Mock ScanOnlyModeImpl mScanOnlyModeImpl;
     @Mock DefaultClientModeManager mDefaultClientModeManager;
+    @Mock WifiCountryCode mWifiCountryCode;
+
     private RegistrationManager.RegistrationCallback mImsMmTelManagerRegistrationCallback = null;
     private @RegistrationManager.ImsRegistrationState int mCurrentImsRegistrationState =
             RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED;
@@ -246,7 +248,8 @@
     private ConcreteClientModeManager createClientModeManager(ActiveModeManager.ClientRole role) {
         return new ConcreteClientModeManager(mContext, mLooper.getLooper(), mClock, mWifiNative,
                 mListener, mWifiMetrics, mWakeupController, mWifiInjector, mSelfRecovery,
-                mWifiGlobals, mDefaultClientModeManager, 0, TEST_WORKSOURCE, role, false);
+                mWifiGlobals, mDefaultClientModeManager, 0, TEST_WORKSOURCE, role, false,
+                mWifiCountryCode);
     }
 
     private void startClientInScanOnlyModeAndVerifyEnabled() throws Exception {
diff --git a/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java b/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
index ae188fc..49c908d 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/SoftApManagerTest.java
@@ -230,7 +230,7 @@
                 .config_wifiFrameworkSoftApShutDownIdleInstanceInBridgedModeTimeoutMillisecond))
                 .thenReturn(
                 (int) TEST_DEFAULT_SHUTDOWN_IDLE_INSTANCE_IN_BRIDGED_MODE_TIMEOUT_MILLS);
-        when(mWifiNative.setCountryCodeHal(
+        when(mWifiNative.setApCountryCode(
                 TEST_INTERFACE_NAME, TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT)))
                 .thenReturn(true);
         when(mWifiNative.getApFactoryMacAddress(any())).thenReturn(TEST_INTERFACE_MAC_ADDRESS);
@@ -440,7 +440,7 @@
 
         mSoftApManager = createSoftApManager(softApConfig, null, ROLE_SOFTAP_TETHERED);
 
-        verify(mWifiNative, never()).setCountryCodeHal(eq(TEST_INTERFACE_NAME), any());
+        verify(mWifiNative, never()).setApCountryCode(eq(TEST_INTERFACE_NAME), any());
 
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
         verify(mContext, times(2)).sendStickyBroadcastAsUser(intentCaptor.capture(),
@@ -468,14 +468,14 @@
                 WifiManager.IFACE_IP_MODE_TETHERED, configBuilder.build(),
                 mTestSoftApCapability);
 
-        when(mWifiNative.setCountryCodeHal(
+        when(mWifiNative.setApCountryCode(
                 TEST_INTERFACE_NAME, TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT)))
                 .thenReturn(false);
 
 
         mSoftApManager = createSoftApManager(softApConfig, TEST_COUNTRY_CODE, ROLE_SOFTAP_TETHERED);
 
-        verify(mWifiNative).setCountryCodeHal(
+        verify(mWifiNative).setApCountryCode(
                 TEST_INTERFACE_NAME, TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT));
 
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -505,7 +505,7 @@
                 mTestSoftApCapability);
 
         startSoftApAndVerifyEnabled(softApConfig, null);
-        verify(mWifiNative, never()).setCountryCodeHal(eq(TEST_INTERFACE_NAME), any());
+        verify(mWifiNative, never()).setApCountryCode(eq(TEST_INTERFACE_NAME), any());
     }
 
     /**
@@ -522,7 +522,7 @@
                 mTestSoftApCapability);
 
         startSoftApAndVerifyEnabled(softApConfig, null);
-        verify(mWifiNative, never()).setCountryCodeHal(eq(TEST_INTERFACE_NAME), any());
+        verify(mWifiNative, never()).setApCountryCode(eq(TEST_INTERFACE_NAME), any());
     }
 
     /**
@@ -538,10 +538,10 @@
                 WifiManager.IFACE_IP_MODE_TETHERED, configBuilder.build(),
                 mTestSoftApCapability);
 
-        when(mWifiNative.setCountryCodeHal(eq(TEST_INTERFACE_NAME), any())).thenReturn(false);
+        when(mWifiNative.setApCountryCode(eq(TEST_INTERFACE_NAME), any())).thenReturn(false);
 
         startSoftApAndVerifyEnabled(softApConfig, TEST_COUNTRY_CODE);
-        verify(mWifiNative).setCountryCodeHal(
+        verify(mWifiNative).setApCountryCode(
                 TEST_INTERFACE_NAME, TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT));
     }
 
@@ -558,10 +558,10 @@
                 WifiManager.IFACE_IP_MODE_TETHERED, configBuilder.build(),
                 mTestSoftApCapability);
 
-        when(mWifiNative.setCountryCodeHal(eq(TEST_INTERFACE_NAME), any())).thenReturn(false);
+        when(mWifiNative.setApCountryCode(eq(TEST_INTERFACE_NAME), any())).thenReturn(false);
 
         startSoftApAndVerifyEnabled(softApConfig, TEST_COUNTRY_CODE);
-        verify(mWifiNative).setCountryCodeHal(
+        verify(mWifiNative).setApCountryCode(
                 TEST_INTERFACE_NAME, TEST_COUNTRY_CODE.toUpperCase(Locale.ROOT));
     }
 
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
index 1cd1486..e870b4f 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiCountryCodeTest.java
@@ -33,6 +33,8 @@
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 
 /**
@@ -51,6 +53,7 @@
     @Mock ActiveModeWarden mActiveModeWarden;
     @Mock ClientModeManager mClientModeManager;
     private WifiCountryCode mWifiCountryCode;
+    private List<ClientModeManager> mClientModeManagers;
 
     /**
      * Setup test.
@@ -59,7 +62,8 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        when(mActiveModeWarden.getPrimaryClientModeManager()).thenReturn(mClientModeManager);
+        mClientModeManagers = Arrays.asList(mClientModeManager, mock(ClientModeManager.class));
+        when(mActiveModeWarden.getClientModeManagers()).thenReturn(mClientModeManagers);
 
         when(mClientModeManager.setCountryCode(anyString())).thenReturn(true);
         when(mContext.getSystemService(Context.TELEPHONY_SERVICE))
diff --git a/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java b/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
index 5691e0b..405dd6a 100644
--- a/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
+++ b/service/tests/wifitests/src/com/android/server/wifi/WifiVendorHalTest.java
@@ -1565,7 +1565,7 @@
      * Test that the country code is set in AP mode (when it should be).
      */
     @Test
-    public void testSetCountryCodeHal() throws Exception {
+    public void testSetApCountryCode() throws Exception {
         byte[] expected = new byte[]{(byte) 'C', (byte) 'A'};
 
         when(mIWifiApIface.setCountryCode(any()))
@@ -1573,12 +1573,12 @@
 
         assertTrue(mWifiVendorHal.startVendorHalAp());
 
-        assertFalse(mWifiVendorHal.setCountryCodeHal(TEST_IFACE_NAME, null));
-        assertFalse(mWifiVendorHal.setCountryCodeHal(TEST_IFACE_NAME, ""));
-        assertFalse(mWifiVendorHal.setCountryCodeHal(TEST_IFACE_NAME, "A"));
+        assertFalse(mWifiVendorHal.setApCountryCode(TEST_IFACE_NAME, null));
+        assertFalse(mWifiVendorHal.setApCountryCode(TEST_IFACE_NAME, ""));
+        assertFalse(mWifiVendorHal.setApCountryCode(TEST_IFACE_NAME, "A"));
         // Only one expected to succeed
-        assertTrue(mWifiVendorHal.setCountryCodeHal(TEST_IFACE_NAME, "CA"));
-        assertFalse(mWifiVendorHal.setCountryCodeHal(TEST_IFACE_NAME, "ZZZ"));
+        assertTrue(mWifiVendorHal.setApCountryCode(TEST_IFACE_NAME, "CA"));
+        assertFalse(mWifiVendorHal.setApCountryCode(TEST_IFACE_NAME, "ZZZ"));
 
         verify(mIWifiApIface).setCountryCode(eq(expected));
     }
@@ -1593,7 +1593,7 @@
         when(mIWifiApIface.setCountryCode(any()))
                 .thenThrow(new RemoteException("oops"));
         assertTrue(mWifiVendorHal.startVendorHalAp());
-        assertFalse(mWifiVendorHal.setCountryCodeHal(TEST_IFACE_NAME, "CA"));
+        assertFalse(mWifiVendorHal.setApCountryCode(TEST_IFACE_NAME, "CA"));
         assertTrue(mWifiVendorHal.isHalStarted());
         verify(mWifiLog).err("% RemoteException in HIDL call %");
     }
@@ -3274,4 +3274,25 @@
             assertScanDataEqual(expected.get(i), actual.get(i));
         }
     }
+
+    /**
+     * Test setCountryCode gets called when the hal version is V1_5.
+     */
+    @Test
+    public void testSetCountryCodeWithHalV1_5() throws Exception {
+        byte[] expected = new byte[]{(byte) 'U', (byte) 'S'};
+        mWifiVendorHal = new WifiVendorHalSpyV1_5(mContext, mHalDeviceManager, mHandler);
+        when(mIWifiChipV15.setCountryCode(any())).thenReturn(mWifiStatusSuccess);
+
+        // Invalid cases
+        assertFalse(mWifiVendorHal.setChipCountryCode(null));
+        assertFalse(mWifiVendorHal.setChipCountryCode(""));
+        assertFalse(mWifiVendorHal.setChipCountryCode("A"));
+        verify(mIWifiChipV15, never()).setCountryCode(any());
+
+        //valid country code
+        assertTrue(mWifiVendorHal.setChipCountryCode("US"));
+        verify(mIWifiChipV15).setCountryCode(eq(expected));
+    }
+
 }