Add callback/listener for satellite communication allowed state changed

Bug: 335760795
Test: atest android.telephony.satellite.cts.SatelliteManagerTestOnMockService
Test: Manually verified if the callback is invoked well when the allowed state is changed in skylo demo mode.

Change-Id: Ia36c8bd1452b2a95dabd5dbba9f364eec057b972
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 4c8c836..f410fad 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -156,6 +156,7 @@
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.satellite.INtnSignalStrengthCallback;
 import android.telephony.satellite.ISatelliteCapabilitiesCallback;
+import android.telephony.satellite.ISatelliteCommunicationAllowedStateCallback;
 import android.telephony.satellite.ISatelliteDatagramCallback;
 import android.telephony.satellite.ISatelliteModemStateCallback;
 import android.telephony.satellite.ISatelliteProvisionStateCallback;
@@ -14082,4 +14083,42 @@
                     methodName + " is unsupported without " + telephonyFeature);
         }
     }
+
+    /**
+     * Registers for the satellite communication allowed state changed.
+     *
+     * @param subId The subId of the subscription to register for the satellite communication
+     *              allowed state changed.
+     * @param callback The callback to handle the satellite communication allowed
+     *                 state changed event.
+     *
+     * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    @SatelliteManager.SatelliteResult public int registerForCommunicationAllowedStateChanged(
+            int subId, @NonNull ISatelliteCommunicationAllowedStateCallback callback) {
+        enforceSatelliteCommunicationPermission("registerForCommunicationAllowedStateChanged");
+        return mSatelliteAccessController.registerForCommunicationAllowedStateChanged(
+                subId, callback);
+    }
+
+    /**
+     * Unregisters for the satellite communication allowed state changed.
+     * If callback was not registered before, the request will be ignored.
+     *
+     * @param subId    The subId of the subscription to unregister for the satellite communication
+     *                 allowed state changed.
+     * @param callback The callback that was passed to
+     *                 {@link #registerForCommunicationAllowedStateChanged(int,
+     *                 ISatelliteCommunicationAllowedStateCallback)}.     *
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    public void unregisterForCommunicationAllowedStateChanged(
+            int subId, @NonNull ISatelliteCommunicationAllowedStateCallback callback) {
+        enforceSatelliteCommunicationPermission("unregisterForCommunicationAllowedStateChanged");
+        mSatelliteAccessController.unregisterForCommunicationAllowedStateChanged(subId, callback);
+    }
 }
diff --git a/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java b/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
index 105dbd5..ea72acd 100644
--- a/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
+++ b/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
@@ -38,8 +38,10 @@
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.SystemClock;
 import android.os.SystemProperties;
@@ -47,6 +49,7 @@
 import android.telecom.TelecomManager;
 import android.telephony.AnomalyReporter;
 import android.telephony.Rlog;
+import android.telephony.satellite.ISatelliteCommunicationAllowedStateCallback;
 import android.telephony.satellite.SatelliteManager;
 import android.text.TextUtils;
 import android.util.Pair;
@@ -79,6 +82,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
@@ -158,12 +162,12 @@
     @NonNull
     private final Map<SatelliteOnDeviceAccessController.LocationToken, Boolean>
             mCachedAccessRestrictionMap = new LinkedHashMap<>() {
-               @Override
-               protected boolean removeEldestEntry(
-                       Entry<SatelliteOnDeviceAccessController.LocationToken, Boolean> eldest) {
-                   return size() > MAX_CACHE_SIZE;
-               }
-           };
+        @Override
+        protected boolean removeEldestEntry(
+                Entry<SatelliteOnDeviceAccessController.LocationToken, Boolean> eldest) {
+            return size() > MAX_CACHE_SIZE;
+        }
+    };
     @GuardedBy("mLock")
     @Nullable
     CancellationSignal mLocationRequestCancellationSignal = null;
@@ -188,6 +192,16 @@
     private SharedPreferences mSharedPreferences;
 
     /**
+     * Map key: binder of the callback, value: callback to receive the satellite communication
+     * allowed state changed events.
+     */
+    private final ConcurrentHashMap<IBinder, ISatelliteCommunicationAllowedStateCallback>
+            mSatelliteCommunicationAllowedStateChangedListeners = new ConcurrentHashMap<>();
+    private final Object mSatelliteCommunicationAllowStateLock = new Object();
+    @GuardedBy("mSatelliteCommunicationAllowStateLock")
+    private boolean mCurrentSatelliteAllowedState = false;
+
+    /**
      * Create a SatelliteAccessController instance.
      *
      * @param context                           The context associated with the
@@ -231,6 +245,18 @@
         initSatelliteOnDeviceAccessController();
     }
 
+    private void updateCurrentSatelliteAllowedState(boolean isAllowed) {
+        logd("updateCurrentSatelliteAllowedState");
+        synchronized (mSatelliteCommunicationAllowStateLock) {
+            if (isAllowed != mCurrentSatelliteAllowedState) {
+                logd("updatedValue = " + isAllowed + " | mCurrentSatelliteAllowedState = "
+                        + mCurrentSatelliteAllowedState);
+                mCurrentSatelliteAllowedState = isAllowed;
+                notifySatelliteCommunicationAllowedStateChanged(isAllowed);
+            }
+        }
+    }
+
     /** @return the singleton instance of {@link SatelliteAccessController} */
     public static synchronized SatelliteAccessController getOrCreateInstance(
             @NonNull Context context, @NonNull FeatureFlags featureFlags) {
@@ -695,21 +721,23 @@
                         Bundle bundle = new Bundle();
                         bundle.putBoolean(SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED,
                                 false);
-                        sendSatelliteAllowResultToReceivers(resultCode, bundle);
+                        sendSatelliteAllowResultToReceivers(resultCode, bundle, false);
                     } else {
                         checkSatelliteAccessRestrictionForCurrentLocation();
                     }
                 } else {
                     loge("KEY_SATELLITE_SUPPORTED does not exist.");
-                    sendSatelliteAllowResultToReceivers(resultCode, resultData);
+                    sendSatelliteAllowResultToReceivers(resultCode, resultData, false);
                 }
             } else {
-                sendSatelliteAllowResultToReceivers(resultCode, resultData);
+                sendSatelliteAllowResultToReceivers(resultCode, resultData, false);
             }
         }
     }
 
-    private void sendSatelliteAllowResultToReceivers(int resultCode, Bundle resultData) {
+    private void sendSatelliteAllowResultToReceivers(int resultCode, Bundle resultData,
+            boolean allowed) {
+        updateCurrentSatelliteAllowedState(allowed);
         synchronized (mLock) {
             for (ResultReceiver resultReceiver : mSatelliteAllowResultReceivers) {
                 resultReceiver.send(resultCode, resultData);
@@ -728,10 +756,10 @@
                 logd("Use current network country codes=" + String.join(", ",
                         networkCountryIsoList));
 
+                boolean allowed = isSatelliteAccessAllowedForLocation(networkCountryIsoList);
                 Bundle bundle = new Bundle();
-                bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED,
-                        isSatelliteAccessAllowedForLocation(networkCountryIsoList));
-                sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle);
+                bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, allowed);
+                sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle, allowed);
             } else {
                 if (shouldUseOnDeviceAccessController()) {
                     // This will be an asynchronous check when it needs to wait for the current
@@ -768,10 +796,10 @@
         }
         logd("Use cached country codes=" + String.join(", ", countryCodeList));
 
+        boolean allowed = isSatelliteAccessAllowedForLocation(countryCodeList);
         Bundle bundle = new Bundle();
-        bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED,
-                isSatelliteAccessAllowedForLocation(countryCodeList));
-        sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle);
+        bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, allowed);
+        sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle, allowed);
     }
 
     /**
@@ -849,7 +877,8 @@
                 }
                 Bundle bundle = new Bundle();
                 bundle.putBoolean(KEY_SATELLITE_COMMUNICATION_ALLOWED, satelliteAllowed);
-                sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle);
+                sendSatelliteAllowResultToReceivers(SATELLITE_RESULT_SUCCESS, bundle,
+                        satelliteAllowed);
             } catch (Exception ex) {
                 loge("checkSatelliteAccessRestrictionForLocation: ex=" + ex);
                 reportAnomaly(UUID_ON_DEVICE_LOOKUP_EXCEPTION,
@@ -1187,6 +1216,66 @@
         msg.sendToTarget();
     }
 
+    /**
+     * Registers for the satellite communication allowed state changed.
+     *
+     * @param subId    The subId of the subscription to register for the satellite communication
+     *                 allowed state changed.
+     * @param callback The callback to handle the satellite communication allowed state changed
+     *                 event.
+     * @return The {@link SatelliteManager.SatelliteResult} result of the operation.
+     */
+    @SatelliteManager.SatelliteResult
+    public int registerForCommunicationAllowedStateChanged(int subId,
+            @NonNull ISatelliteCommunicationAllowedStateCallback callback) {
+        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+            logd("registerForCommunicationAllowedStateChanged: oemEnabledSatelliteFlag is "
+                    + "disabled");
+            return SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
+        }
+
+        mSatelliteCommunicationAllowedStateChangedListeners.put(callback.asBinder(), callback);
+        return SATELLITE_RESULT_SUCCESS;
+    }
+
+    /**
+     * Unregisters for the satellite communication allowed state changed.
+     * If callback was not registered before, the request will be ignored.
+     *
+     * @param subId    The subId of the subscription to unregister for the satellite communication
+     *                 allowed state changed.
+     * @param callback The callback that was passed to
+     *                 {@link #registerForCommunicationAllowedStateChanged(int,
+     *                 ISatelliteCommunicationAllowedStateCallback)}.
+     */
+    public void unregisterForCommunicationAllowedStateChanged(
+            int subId, @NonNull ISatelliteCommunicationAllowedStateCallback callback) {
+        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
+            logd("unregisterForCommunicationAllowedStateChanged: "
+                    + "oemEnabledSatelliteFlag is disabled");
+            return;
+        }
+
+        mSatelliteCommunicationAllowedStateChangedListeners.remove(callback.asBinder());
+    }
+
+    private void notifySatelliteCommunicationAllowedStateChanged(boolean allowState) {
+        logd("notifySatelliteCommunicationAllowedStateChanged: allowState=" + allowState);
+
+        List<ISatelliteCommunicationAllowedStateCallback> deadCallersList = new ArrayList<>();
+        mSatelliteCommunicationAllowedStateChangedListeners.values().forEach(listener -> {
+            try {
+                listener.onSatelliteCommunicationAllowedStateChanged(allowState);
+            } catch (RemoteException e) {
+                logd("handleEventNtnSignalStrengthChanged RemoteException: " + e);
+                deadCallersList.add(listener);
+            }
+        });
+        deadCallersList.forEach(listener -> {
+            mSatelliteCommunicationAllowedStateChangedListeners.remove(listener.asBinder());
+        });
+    }
+
     private static void logd(@NonNull String log) {
         Rlog.d(TAG, log);
     }