wifi: Add Country code event listener support

1. Register WificondEventCallback when wificond init.
The event callback can be used for any generic NL80211 event, like
country code.

2. Create countryCodeListener for framework WifiCountryCode.
Setup listener into WificondEventCallback to get country code changed
event.

Bug: 177630837
CTS-Coverage-Bug: 177630837
Test: atest -c FrameworksWifiNonUpdatableApiTests
Test: Manual Test, country code change can pass to framework.
Change-Id: I8c250122415aafc5b8e4f87409b8ebfcf2e795e9
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 115edd15..37e2445 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -7775,6 +7775,7 @@
     method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.TxPacketCounters getTxPacketCounters(@NonNull String);
     method @Nullable public static android.net.wifi.nl80211.WifiNl80211Manager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
     method @Deprecated public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SoftApCallback);
+    method public boolean registerCountryCodeChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangeListener);
     method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.SendMgmtFrameCallback);
     method public void setOnServiceDeadCallback(@NonNull Runnable);
     method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.ScanEventCallback);
@@ -7787,6 +7788,7 @@
     method public boolean tearDownClientInterface(@NonNull String);
     method public boolean tearDownInterfaces();
     method public boolean tearDownSoftApInterface(@NonNull String);
+    method public void unregisterCountryCodeChangeListener(@NonNull android.net.wifi.nl80211.WifiNl80211Manager.CountryCodeChangeListener);
     field public static final String SCANNING_PARAM_ENABLE_6GHZ_RNR = "android.net.wifi.nl80211.SCANNING_PARAM_ENABLE_6GHZ_RNR";
     field public static final int SCAN_TYPE_PNO_SCAN = 1; // 0x1
     field public static final int SCAN_TYPE_SINGLE_SCAN = 0; // 0x0
@@ -7797,6 +7799,10 @@
     field public static final int SEND_MGMT_FRAME_ERROR_UNKNOWN = 1; // 0x1
   }
 
+  public static interface WifiNl80211Manager.CountryCodeChangeListener {
+    method public void onChanged(@NonNull String);
+  }
+
   public static class WifiNl80211Manager.OemSecurityType {
     ctor public WifiNl80211Manager.OemSecurityType(int, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>, int);
     field public final int groupCipher;
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index c64f4bc..da0571ba 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -101,6 +101,7 @@
 
     // Cached wificond binder handlers.
     private IWificond mWificond;
+    private WificondEventHandler mWificondEventHandler = new WificondEventHandler();
     private HashMap<String, IClientInterface> mClientInterfaces = new HashMap<>();
     private HashMap<String, IApInterface> mApInterfaces = new HashMap<>();
     private HashMap<String, IWifiScannerImpl> mWificondScanners = new HashMap<>();
@@ -114,6 +115,18 @@
     private AtomicBoolean mSendMgmtFrameInProgress = new AtomicBoolean(false);
 
     /**
+     * Interface used to listen country code event
+     */
+    public interface CountryCodeChangeListener {
+        /**
+         * Called when country code changed.
+         *
+         * @param countryCode A new country code which is 2-Character alphanumeric.
+         */
+        void onChanged(@NonNull String countryCode);
+    }
+
+    /**
      * Interface used when waiting for scans to be completed (with results).
      */
     public interface ScanEventCallback {
@@ -147,6 +160,46 @@
         void onPnoRequestFailed();
     }
 
+    /** @hide */
+    @VisibleForTesting
+    public class WificondEventHandler extends IWificondEventCallback.Stub {
+        private Map<CountryCodeChangeListener, Executor> mCountryCodeChangeListenerHolder =
+                new HashMap<>();
+
+        /**
+         * Register CountryCodeChangeListener with pid.
+         *
+         * @param executor The Executor on which to execute the callbacks.
+         * @param listener listener for country code changed events.
+         */
+        public void registerCountryCodeChangeListener(Executor executor,
+                CountryCodeChangeListener listener) {
+            mCountryCodeChangeListenerHolder.put(listener, executor);
+        }
+
+        /**
+         * Unregister CountryCodeChangeListener with pid.
+         *
+         * @param listener listener which registered country code changed events.
+         */
+        public void unregisterCountryCodeChangeListener(CountryCodeChangeListener listener) {
+            mCountryCodeChangeListenerHolder.remove(listener);
+        }
+
+        @Override
+        public void OnRegDomainChanged(String countryCode) {
+            Log.d(TAG, "OnRegDomainChanged " + countryCode);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                mCountryCodeChangeListenerHolder.forEach((listener, executor) -> {
+                    executor.execute(() -> listener.onChanged(countryCode));
+                });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+    }
+
     private class ScanEventHandler extends IScanEvent.Stub {
         private Executor mExecutor;
         private ScanEventCallback mCallback;
@@ -347,6 +400,12 @@
         mWificond = wificond;
     }
 
+    /** @hide */
+    @VisibleForTesting
+    public WificondEventHandler getWificondEventHandler() {
+        return mWificondEventHandler;
+    }
+
     private class PnoScanEventHandler extends IPnoScanEvent.Stub {
         private Executor mExecutor;
         private ScanEventCallback mCallback;
@@ -574,6 +633,7 @@
         }
         try {
             mWificond.asBinder().linkToDeath(() -> binderDied(), 0);
+            mWificond.registerWificondEventCallback(mWificondEventHandler);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to register death notification for wificond");
             // The remote has already died.
@@ -1174,6 +1234,34 @@
     }
 
     /**
+     * Register the provided listener for country code event.
+     *
+     * @param executor The Executor on which to execute the callbacks.
+     * @param listener listener for country code changed events.
+     * @return true on success, false on failure.
+     */
+    public boolean registerCountryCodeChangeListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull CountryCodeChangeListener listener) {
+        if (!retrieveWificondAndRegisterForDeath()) {
+            return false;
+        }
+        Log.d(TAG, "registerCountryCodeEventListener called");
+        mWificondEventHandler.registerCountryCodeChangeListener(executor, listener);
+        return true;
+    }
+
+
+    /**
+     * Unregister CountryCodeChangeListener with pid.
+     *
+     * @param listener listener which registered country code changed events.
+     */
+    public void unregisterCountryCodeChangeListener(@NonNull CountryCodeChangeListener listener) {
+        Log.d(TAG, "unregisterCountryCodeEventListener called");
+        mWificondEventHandler.unregisterCountryCodeChangeListener(listener);
+    }
+
+    /**
      * Register the provided callback handler for SoftAp events. The interface must first be created
      * using {@link #setupInterfaceForSoftApMode(String)}. The callback registration is valid until
      * the interface is deleted using {@link #tearDownSoftApInterface(String)} (no deregistration
diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
index 4b03a49..98a0042 100644
--- a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
@@ -30,6 +30,7 @@
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -97,14 +98,20 @@
     @Mock
     private WifiNl80211Manager.PnoScanRequestCallback mPnoScanRequestCallback;
     @Mock
+    private WifiNl80211Manager.CountryCodeChangeListener mCountryCodeChangeListener;
+    @Mock
+    private WifiNl80211Manager.CountryCodeChangeListener mCountryCodeChangeListener2;
+    @Mock
     private Context mContext;
     private TestLooper mLooper;
     private TestAlarmManager mTestAlarmManager;
     private AlarmManager mAlarmManager;
     private WifiNl80211Manager mWificondControl;
+    private WifiNl80211Manager.WificondEventHandler mWificondEventHandler;
     private static final String TEST_INTERFACE_NAME = "test_wlan_if";
     private static final String TEST_INTERFACE_NAME1 = "test_wlan_if1";
     private static final String TEST_INVALID_INTERFACE_NAME = "asdf";
+    private static final String TEST_COUNTRY_CODE = "US";
     private static final byte[] TEST_SSID =
             new byte[]{'G', 'o', 'o', 'g', 'l', 'e', 'G', 'u', 'e', 's', 't'};
     private static final byte[] TEST_PSK =
@@ -182,6 +189,7 @@
         when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl);
         when(mClientInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME);
         mWificondControl = new WifiNl80211Manager(mContext, mWificond);
+        mWificondEventHandler = mWificondControl.getWificondEventHandler();
         assertEquals(true,
                 mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, Runnable::run,
                         mNormalScanCallback, mPnoScanCallback));
@@ -760,6 +768,28 @@
     }
 
     /**
+     * Ensures callback works after register CountryCodeChangeListener.
+     */
+    @Test
+    public void testCountryCodeChangeListenerInvocation() throws Exception {
+        assertTrue(mWificondControl.registerCountryCodeChangeListener(
+                Runnable::run, mCountryCodeChangeListener));
+        assertTrue(mWificondControl.registerCountryCodeChangeListener(
+                Runnable::run, mCountryCodeChangeListener2));
+
+        mWificondEventHandler.OnRegDomainChanged(TEST_COUNTRY_CODE);
+        verify(mCountryCodeChangeListener).onChanged(TEST_COUNTRY_CODE);
+        verify(mCountryCodeChangeListener2).onChanged(TEST_COUNTRY_CODE);
+
+        reset(mCountryCodeChangeListener);
+        reset(mCountryCodeChangeListener2);
+        mWificondControl.unregisterCountryCodeChangeListener(mCountryCodeChangeListener2);
+        mWificondEventHandler.OnRegDomainChanged(TEST_COUNTRY_CODE);
+        verify(mCountryCodeChangeListener).onChanged(TEST_COUNTRY_CODE);
+        verify(mCountryCodeChangeListener2, never()).onChanged(TEST_COUNTRY_CODE);
+    }
+
+    /**
      * Verifies registration and invocation of wificond death handler.
      */
     @Test