Merge "Change the thread of onSelectionTerminated"
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index cf1f784..26eb8c2 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -148,6 +148,7 @@
 import android.telephony.satellite.ISatelliteStateListener;
 import android.telephony.satellite.PointingInfo;
 import android.telephony.satellite.SatelliteCapabilities;
+import android.telephony.satellite.SatelliteDatagram;
 import android.telephony.satellite.SatelliteManager;
 import android.text.TextUtils;
 import android.util.ArraySet;
@@ -392,8 +393,8 @@
     private static final int EVENT_GET_MAX_CHAR_PER_SATELLITE_TEXT_MSG_DONE = 124;
     private static final int CMD_PROVISION_SATELLITE_SERVICE = 125;
     private static final int EVENT_PROVISION_SATELLITE_SERVICE_DONE = 126;
-    private static final int CMD_CANCEL_PROVISION_SATELLITE_SERVICE = 127;
-    private static final int EVENT_CANCEL_PROVISION_SATELLITE_SERVICE_DONE = 128;
+    private static final int CMD_DEPROVISION_SATELLITE_SERVICE = 127;
+    private static final int EVENT_DEPROVISION_SATELLITE_SERVICE_DONE = 128;
     private static final int CMD_SET_SATELLITE_ENABLED = 129;
     private static final int EVENT_SET_SATELLITE_ENABLED_DONE = 130;
     private static final int CMD_IS_SATELLITE_ENABLED = 131;
@@ -402,6 +403,9 @@
     private static final int EVENT_IS_SATELLITE_SUPPORTED_DONE = 134;
     private static final int CMD_GET_SATELLITE_CAPABILITIES = 135;
     private static final int EVENT_GET_SATELLITE_CAPABILITIES_DONE = 136;
+    private static final int CMD_POLL_PENDING_SATELLITE_DATAGRAMS = 137;
+    private static final int EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE = 138;
+
     // Parameters of select command.
     private static final int SELECT_COMMAND = 0xA4;
     private static final int SELECT_P1 = 0x04;
@@ -453,6 +457,18 @@
     private final Object mIsSatelliteSupportedLock = new Object();
     private final ResultReceiver mSatelliteSupportedReceiver;
 
+    /**
+     * Map key: subId, value: SatelliteStateChangeHandler to notify registrants.
+     */
+    private ConcurrentHashMap<Integer, SatelliteStateListenerHandler>
+            mSatelliteStateListenerHandlers = new ConcurrentHashMap<>();
+
+    /**
+     * Map key: subId, value: SatelliteDatagramListenerHandler to notify registrants.
+     */
+    private ConcurrentHashMap<Integer, SatelliteDatagramListenerHandler>
+            mSatelliteDatagramListenerHandlers = new ConcurrentHashMap<>();
+
     private static final String PREF_CARRIERS_ALPHATAG_PREFIX = "carrier_alphtag_";
     private static final String PREF_CARRIERS_NUMBER_PREFIX = "carrier_number_";
     private static final String PREF_CARRIERS_SUBSCRIBER_PREFIX = "carrier_subscriber_";
@@ -625,13 +641,11 @@
                 case EVENT_PROVISION_STATE_CHANGED: {
                     AsyncResult ar = (AsyncResult) msg.obj;
                     boolean provisioned = (boolean) ar.userObj;
-                    int[] features = (int[]) ar.result;
                     log("Received EVENT_PROVISION_STATE_CHANGED for subId=" + mSubId
-                            + ", features=" + Arrays.toString(features)
                             + ", provisioned=" + provisioned);
                     mListeners.values().forEach(listener -> {
                         try {
-                            listener.onSatelliteProvisionStateChanged(features, provisioned);
+                            listener.onSatelliteProvisionStateChanged(provisioned);
                         } catch (RemoteException e) {
                             log("EVENT_PROVISION_STATE_CHANGED RemoteException: " + e);
                         }
@@ -659,6 +673,109 @@
         }
     }
 
+    private static final class SatelliteStateListenerHandler extends Handler {
+        public static final int EVENT_SATELLITE_MODEM_STATE_CHANGE = 1;
+        public static final int EVENT_PENDING_MESSAGE_COUNT = 2;
+
+        private ConcurrentHashMap<IBinder, ISatelliteStateListener> mListeners;
+        private final int mSubId;
+
+        SatelliteStateListenerHandler(Looper looper, int subId) {
+            super(looper);
+            mSubId = subId;
+            mListeners = new ConcurrentHashMap<>();
+        }
+
+        public void addListener(ISatelliteStateListener listener) {
+            mListeners.put(listener.asBinder(), listener);
+        }
+
+        public boolean removeListener(ISatelliteStateListener listener) {
+            return (mListeners.remove(listener.asBinder()) != null);
+        }
+
+        @Override
+        public void handleMessage(@NonNull Message msg) {
+            switch (msg.what) {
+                case EVENT_SATELLITE_MODEM_STATE_CHANGE : {
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    int state = (int) ar.result;
+                    log("Received EVENT_SATELLITE_MODEM_STATE_CHANGE for subId=" + mSubId
+                            + ", state=" + state);
+                    mListeners.values().forEach(listener -> {
+                        try {
+                            listener.onSatelliteModemStateChange(state);
+                        } catch (RemoteException e) {
+                            log("EVENT_SATELLITE_MODEM_STATE_CHANGE RemoteException: " + e);
+                        }
+                    });
+                    break;
+                }
+                case EVENT_PENDING_MESSAGE_COUNT: {
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    int count = (int) ar.result;
+                    log("Received EVENT_PENDING_MESSAGE_COUNT for subId=" + mSubId
+                            + ", count=" + count);
+                    mListeners.values().forEach(listener -> {
+                        try {
+                            listener.onPendingMessageCount(count);
+                        } catch (RemoteException e) {
+                            log("EVENT_PENDING_MESSAGE_COUNT RemoteException: " + e);
+                        }
+                    });
+                    break;
+                }
+                default:
+                    loge("SatelliteStateListenerHandler unknown event: " + msg.what);
+            }
+        }
+    }
+
+    private static final class SatelliteDatagramListenerHandler extends Handler {
+        public static final int EVENT_SATELLITE_DATAGRAMS_RECEIVED = 1;
+
+        private ConcurrentHashMap<IBinder, ISatelliteStateListener> mListeners;
+        private final int mSubId;
+
+        SatelliteDatagramListenerHandler(Looper looper, int subId) {
+            super(looper);
+            mSubId = subId;
+            mListeners = new ConcurrentHashMap<>();
+        }
+
+        public void addListener(ISatelliteStateListener listener) {
+            mListeners.put(listener.asBinder(), listener);
+        }
+
+        public boolean removeListener(ISatelliteStateListener listener) {
+            return (mListeners.remove(listener.asBinder()) != null);
+        }
+
+        @Override
+        public void handleMessage(@NonNull Message msg) {
+            switch (msg.what) {
+                case EVENT_SATELLITE_DATAGRAMS_RECEIVED : {
+                    AsyncResult ar = (AsyncResult) msg.obj;
+                    byte[][] datagrams = (byte[][]) ar.result;
+                    SatelliteDatagram[] satelliteDatagramArray =
+                            convertToSatelliteDatagramArray(datagrams);
+
+                    log("Received EVENT_SATELLITE_DATAGRAMS_RECEIVED for subId=" + mSubId);
+                    mListeners.values().forEach(listener -> {
+                        try {
+                            listener.onSatelliteDatagrams(satelliteDatagramArray);
+                        } catch (RemoteException e) {
+                            log("EVENT_SATELLITE_DATAGRAMS_RECEIVED RemoteException: " + e);
+                        }
+                    });
+                    break;
+                }
+                default:
+                    loge("SatelliteDatagramListenerHandler unknown event: " + msg.what);
+            }
+        }
+    }
+
     /**
      * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
      * request after sending. The main thread will notify the request when it is complete.
@@ -2505,42 +2622,37 @@
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
                     int errorCode = getSatelliteError(ar, "provisionSatelliteService", false);
-                    ProvisionSatelliteServiceArgument argument =
-                            (ProvisionSatelliteServiceArgument) request.argument;
-                    handleEventProvisionSatelliteServiceDone(argument.subId, errorCode);
+                    handleEventProvisionSatelliteServiceDone(
+                            (ProvisionSatelliteServiceArgument) request.argument, errorCode);
                     notifyRequester(request);
                     break;
                 }
 
-                case CMD_CANCEL_PROVISION_SATELLITE_SERVICE: {
+                case CMD_DEPROVISION_SATELLITE_SERVICE: {
                     request = (MainThreadRequest) msg.obj;
-                    onCompleted =
-                            obtainMessage(EVENT_CANCEL_PROVISION_SATELLITE_SERVICE_DONE, request);
-                    handleCmdCancelProvisionSatelliteService((int) request.argument, onCompleted);
+                    onCompleted = obtainMessage(EVENT_DEPROVISION_SATELLITE_SERVICE_DONE, request);
+                    Phone phone = getPhoneFromRequest(request);
+                    ProvisionSatelliteServiceArgument argument =
+                            (ProvisionSatelliteServiceArgument) request.argument;
+                    if (phone != null) {
+                        handleCmdDeprovisionSatelliteService(argument, phone, onCompleted);
+                    } else {
+                        loge("deprovisionSatelliteService: No phone object");
+                        if (argument.callback != null) {
+                            argument.callback.accept(
+                                    SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                        }
+                        notifyRequester(request);
+                    }
                     break;
                 }
 
-                case EVENT_CANCEL_PROVISION_SATELLITE_SERVICE_DONE: {
+                case EVENT_DEPROVISION_SATELLITE_SERVICE_DONE: {
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
-
-                    if (ar.exception == null) {
-                        request.result = SatelliteManager.SATELLITE_ERROR_NONE;
-                        log("cancelProvisionSatelliteService succeeded for subId="
-                                + (int) request.argument);
-                    } else {
-                        request.result = SatelliteManager.SATELLITE_ERROR;
-                        if (ar.exception instanceof CommandException) {
-                            CommandException.Error error =
-                                    ((CommandException) (ar.exception)).getCommandError();
-                            request.result = RILUtils.convertToSatelliteError(error);
-                            loge("cancelProvisionSatelliteService CommandException: "
-                                    + ar.exception + ", error code:" + request.result);
-                        } else {
-                            loge("cancelProvisionSatelliteService unknown exception: "
-                                    + ar.exception);
-                        }
-                    }
+                    int errorCode = getSatelliteError(ar, "deprovisionSatelliteService", false);
+                    handleEventDeprovisionSatelliteServiceDone(
+                            (ProvisionSatelliteServiceArgument) request.argument, errorCode);
                     notifyRequester(request);
                     break;
                 }
@@ -2664,6 +2776,48 @@
                     break;
                 }
 
+                case CMD_POLL_PENDING_SATELLITE_DATAGRAMS: {
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE,
+                            request);
+                    Phone phone = getPhoneFromRequest(request);
+                    if (phone != null) {
+                        phone.pollPendingSatelliteDatagrams(onCompleted);
+                    } else {
+                        loge("pollPendingSatelliteDatagrams: No phone object");
+                        request.result = SatelliteManager
+                                .SATELLITE_INVALID_TELEPHONY_STATE;
+                        notifyRequester(request);
+                    }
+                    break;
+                }
+
+                case EVENT_POLL_PENDING_SATELLITE_DATAGRAMS_DONE: {
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    if (ar.exception != null) {
+                        request.result = SatelliteManager.SATELLITE_SERVICE_ERROR;
+                        if (ar.exception instanceof CommandException) {
+                            CommandException.Error error =
+                                    ((CommandException) (ar.exception)).getCommandError();
+                            request.result = RILUtils.convertToSatelliteError(error);
+                            loge("pollPendingSatelliteDatagrams: "
+                                    + "CommandException: " + ar.exception);
+                        } else {
+                            loge("pollPendingSatelliteDatagrams: "
+                                    + "unknown exception:" + ar.exception);
+                        }
+                    } else if (ar.result == null) {
+                        request.result = SatelliteManager
+                                .SATELLITE_INVALID_TELEPHONY_STATE;
+                        loge("pollPendingSatelliteDatagrams: result is null");
+                    } else {
+                        request.result = SatelliteManager.SATELLITE_ERROR_NONE;
+                    }
+                    notifyRequester(request);
+                    break;
+                }
+
                 default:
                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
                     break;
@@ -12761,7 +12915,8 @@
      * This is needed to register the subscription if the provider allows dynamic registration.
      *
      * @param subId The subId of the subscription to be provisioned.
-     * @param token The security token of the device/subscription to be provisioned.
+     * @param token The token to be used as a unique identifier for provisioning with satellite
+     *              gateway.
      * @param callback The callback to get the error code of the request.
      * @return The signal transport used by the caller to cancel the provision request,
      *         or {@code null} if the request failed.
@@ -12799,14 +12954,53 @@
                 new ProvisionSatelliteServiceArgument(token, result, validSubId), phone, null);
 
         ICancellationSignal cancelTransport = CancellationSignal.createTransport();
-        CancellationSignal.fromTransport(cancelTransport)
-                .setOnCancelListener(() -> {
-                    sendRequestAsync(CMD_CANCEL_PROVISION_SATELLITE_SERVICE, validSubId);
-                });
+        CancellationSignal.fromTransport(cancelTransport).setOnCancelListener(() -> {
+            sendRequestAsync(CMD_DEPROVISION_SATELLITE_SERVICE,
+                    new ProvisionSatelliteServiceArgument(token, null, validSubId),
+                    phone, null);
+        });
         return cancelTransport;
     }
 
     /**
+     * Unregister the device/subscription with the satellite provider.
+     * This is needed if the provider allows dynamic registration. Once deprovisioned,
+     * {@link SatelliteCallback.SatelliteProvisionStateListener#onSatelliteProvisionStateChanged}
+     * should report as deprovisioned.
+     *
+     * @param subId The subId of the subscription to be deprovisioned.
+     * @param token The token of the device/subscription to be deprovisioned.
+     * @param callback The callback to get the error code of the request.
+     *
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    public void deprovisionSatelliteService(int subId,
+            @NonNull String token, @NonNull IIntegerConsumer callback) {
+        enforceSatelliteCommunicationPermission("deprovisionSatelliteService");
+        Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
+        if (!isSatelliteSupported()) {
+            result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+            return;
+        }
+
+        final int validSubId = getValidSatelliteSubId(subId);
+        if (!isSatelliteProvisioned(validSubId)) {
+            result.accept(SatelliteManager.SATELLITE_ERROR_NONE);
+            return;
+        }
+
+        Phone phone = getPhoneOrDefault(validSubId, "deprovisionSatelliteService");
+        if (phone == null) {
+            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            return;
+        }
+
+        sendRequestAsync(CMD_DEPROVISION_SATELLITE_SERVICE,
+                new ProvisionSatelliteServiceArgument(token, result, validSubId), phone, null);
+    }
+
+    /**
      * Register for the satellite provision state change.
      *
      * @param subId The subId of the subscription associated with the satellite service.
@@ -12819,43 +13013,7 @@
     public void registerForSatelliteProvisionStateChanged(int subId,
             @NonNull IIntegerConsumer errorCallback, @NonNull ISatelliteStateListener callback) {
         enforceSatelliteCommunicationPermission("registerForSatelliteProvisionStateChanged");
-        Consumer<Integer> result = null;
-        if (errorCallback != null) {
-            result = FunctionalUtils.ignoreRemoteException(errorCallback::accept);
-        }
-        if (!isSatelliteSupported()) {
-            if (result != null) {
-                result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
-            }
-            return;
-        }
-
-        final int validSubId = getValidSatelliteSubId(subId);
-        Phone phone = getPhoneOrDefault(validSubId, "registerForSatelliteProvisionStateChanged");
-        if (phone == null) {
-            if (result != null) {
-                result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
-            }
-            return;
-        }
-
-        SatelliteProvisionStateChangedHandler satelliteProvisionStateChangedHandler =
-                mSatelliteProvisionStateChangedHandlers.get(validSubId);
-        if (satelliteProvisionStateChangedHandler == null) {
-            satelliteProvisionStateChangedHandler = new SatelliteProvisionStateChangedHandler(
-                    Looper.getMainLooper(), validSubId);
-            phone.registerForSatelliteProvisionStateChanged(satelliteProvisionStateChangedHandler,
-                    SatelliteProvisionStateChangedHandler.EVENT_PROVISION_STATE_CHANGED, null);
-        }
-
-        if (callback != null) {
-            satelliteProvisionStateChangedHandler.addListener(callback);
-        }
-        mSatelliteProvisionStateChangedHandlers.put(
-                validSubId, satelliteProvisionStateChangedHandler);
-        if (result != null) {
-            result.accept(SatelliteManager.SATELLITE_ERROR_NONE);
-        }
+        registerForSatelliteProvisionStateChangedInternal(subId, errorCallback, callback);
     }
 
     /**
@@ -12915,43 +13073,187 @@
         result.send(SatelliteManager.SATELLITE_ERROR_NONE, bundle);
     }
 
-    private void handleCmdProvisionSatelliteService(@NonNull ProvisionSatelliteServiceArgument arg,
-            @NonNull Phone phone, Message onCompleted) {
-        if (arg == null) {
-            loge("handleCmdProvisionSatelliteService: arg is null");
-            return;
-        }
+    /**
+     * Register for listening to satellite state changes.
+     *
+     * @param subId - The subId to start listening to satellite state changes for.
+     * @param callback - The callback to handle the satellite state change event.
+     * @return The result of the operation.
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    @SatelliteManager.SatelliteError
+    public int registerForSatelliteModemStateChange(int subId,
+            @NonNull ISatelliteStateListener callback) {
+        enforceSatelliteCommunicationPermission("registerForSatelliteModemStateChange");
+
+        final int validSubId = getValidSatelliteSubId(subId);
+        Phone phone = getPhoneOrDefault(
+                validSubId, "registerForSatelliteModemStateChange");
         if (phone == null) {
-            loge("handleCmdProvisionSatelliteService: phone is null");
-            arg.callback.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
-            return;
+            return SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
         }
 
+        SatelliteStateListenerHandler satelliteStateListenerHandler =
+                mSatelliteStateListenerHandlers.get(validSubId);
+        if (satelliteStateListenerHandler == null) {
+            satelliteStateListenerHandler = new SatelliteStateListenerHandler(
+                    Looper.getMainLooper(), validSubId);
+            phone.registerForSatelliteModemStateChange(satelliteStateListenerHandler,
+                    SatelliteStateListenerHandler.EVENT_SATELLITE_MODEM_STATE_CHANGE, null);
+            phone.registerForPendingMessageCount(satelliteStateListenerHandler,
+                    SatelliteStateListenerHandler.EVENT_PENDING_MESSAGE_COUNT, null);
+        }
+
+        satelliteStateListenerHandler.addListener(callback);
+        mSatelliteStateListenerHandlers.put(validSubId, satelliteStateListenerHandler);
+        return SatelliteManager.SATELLITE_ERROR_NONE;
+    }
+
+    /**
+     * Unregister from listening to satellite state changes.
+     *
+     * @param subId -  The subId to stop listening to satellite state changes for.
+     * @param callback - The callback that was passed to
+     * {@link #registerForSatelliteModemStateChange(int, ISatelliteStateListener)}
+     * @return The result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    @SatelliteManager.SatelliteError
+    public int unregisterForSatelliteModemStateChange(int subId,
+            @NonNull ISatelliteStateListener callback) {
+        enforceSatelliteCommunicationPermission("unregisterForSatelliteModemStateChange");
+
+        final int validSubId = getValidSatelliteSubId(subId);
+        SatelliteStateListenerHandler satelliteStateListenerHandler =
+                mSatelliteStateListenerHandlers.get(validSubId);
+        if (satelliteStateListenerHandler != null) {
+            if (satelliteStateListenerHandler.removeListener(callback)) {
+                return SatelliteManager.SATELLITE_ERROR_NONE;
+            }
+        }
+        return SatelliteManager.SATELLITE_INVALID_ARGUMENTS;
+    }
+
+    /**
+     * Register to receive incoming datagrams over satellite.
+     *
+     * @param subId - The subId to start receiving datagrams over satellite.
+     * @param datagramType - type of datagram
+     * @param callback - The callback to handle incoming datagrams over satellite.
+     * @return The result of the operation.
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    @SatelliteManager.SatelliteError
+    public int registerForSatelliteDatagram(int subId,
+            @SatelliteManager.DatagramType int datagramType,
+            @NonNull ISatelliteStateListener callback) {
+        enforceSatelliteCommunicationPermission("registerForSatelliteDatagram");
+
+        final int validSubId = getValidSatelliteSubId(subId);
+        Phone phone = getPhoneOrDefault(
+                validSubId, "registerForSatelliteDatagram");
+        if (phone == null) {
+            return SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+        }
+
+        SatelliteDatagramListenerHandler satelliteDatagramListenerHandler =
+                mSatelliteDatagramListenerHandlers.get(validSubId);
+        if (satelliteDatagramListenerHandler == null) {
+            satelliteDatagramListenerHandler = new SatelliteDatagramListenerHandler(
+                    Looper.getMainLooper(), validSubId);
+            phone.registerForNewSatelliteDatagram(satelliteDatagramListenerHandler,
+                    SatelliteDatagramListenerHandler.EVENT_SATELLITE_DATAGRAMS_RECEIVED, null);
+        }
+
+        satelliteDatagramListenerHandler.addListener(callback);
+        mSatelliteDatagramListenerHandlers.put(validSubId, satelliteDatagramListenerHandler);
+        return SatelliteManager.SATELLITE_ERROR_NONE;
+    }
+
+    /**
+     * Unregister to stop receiving incoming datagrams over satellite.
+     *
+     * @param subId -  The subId to stop listening incoming datagrams over satellite.
+     * @param callback - The callback that was passed to
+     * {@link #registerForSatelliteDatagram(int, int, ISatelliteStateListener)}
+     * @return The result of the operation.
+     *
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    @SatelliteManager.SatelliteError
+    public int unregisterForSatelliteDatagram(int subId,
+            @NonNull ISatelliteStateListener callback) {
+        enforceSatelliteCommunicationPermission("unregisterForSatelliteDatagram");
+
+        final int validSubId = getValidSatelliteSubId(subId);
+        SatelliteDatagramListenerHandler satelliteDatagramListenerHandler =
+                mSatelliteDatagramListenerHandlers.get(validSubId);
+        if (satelliteDatagramListenerHandler != null) {
+            if (satelliteDatagramListenerHandler.removeListener(callback)) {
+                return SatelliteManager.SATELLITE_ERROR_NONE;
+            }
+        }
+        return SatelliteManager.SATELLITE_INVALID_ARGUMENTS;
+    }
+
+
+    /**
+     * Poll pending satellite datagrams over satellite.
+     * @param subId - The subId of the subscription used for receiving datagrams.
+     * @return The result of the operation.
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    @SatelliteManager.SatelliteError
+    public int pollPendingSatelliteDatagrams(int subId) {
+        enforceSatelliteCommunicationPermission("pollPendingSatelliteDatagrams");
+
+        final int validSubId = getValidSatelliteSubId(subId);
+        if (!isSatelliteProvisioned(validSubId)) {
+            return SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED;
+        }
+
+        Phone phone = getPhoneOrDefault(validSubId, "pollPendingSatelliteDatagrams");
+        if (phone == null) {
+            return SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+        }
+
+        int result = (int) sendRequest(CMD_POLL_PENDING_SATELLITE_DATAGRAMS, null, validSubId);
+        if (DBG) log("pollPendingSatelliteDatagrams result: " + result);
+        return result;
+    }
+
+    private void handleCmdProvisionSatelliteService(@NonNull ProvisionSatelliteServiceArgument arg,
+            @NonNull Phone phone, Message onCompleted) {
         if (!mSatelliteProvisionCallbacks.containsKey(arg.subId)) {
             mSatelliteProvisionCallbacks.put(arg.subId, arg.callback);
-            phone.provisionSatelliteService(onCompleted, arg.token, phone.getImei(),
-                    phone.getMsisdn(), getSatelliteImsi(arg.subId));
+            phone.provisionSatelliteService(onCompleted, arg.token);
         } else {
             arg.callback.accept(SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS);
         }
     }
 
-    private void handleEventProvisionSatelliteServiceDone(int subId,
+    private void handleEventProvisionSatelliteServiceDone(
+            @NonNull ProvisionSatelliteServiceArgument arg,
             @SatelliteManager.SatelliteError int result) {
-        Consumer<Integer> callback = mSatelliteProvisionCallbacks.remove(subId);
+        log("handleEventProvisionSatelliteServiceDone: result="
+                + result + ", subId=" + arg.subId);
+
+        Consumer<Integer> callback = mSatelliteProvisionCallbacks.remove(arg.subId);
         if (callback == null) {
-            loge("handleEventProvisionSatelliteServiceDone: callback is null for subId=" + subId);
+            loge("handleEventProvisionSatelliteServiceDone: callback is null for subId="
+                    + arg.subId);
             return;
         }
         callback.accept(result);
 
-        if (DBG) {
-            log("handleEventProvisionSatelliteServiceDone: result=" + result + ", subId="
-                    + subId);
-        }
-
         if (result == SatelliteManager.SATELLITE_ERROR_NONE) {
-            setSatelliteProvisioned(subId, true);
+            setSatelliteProvisioned(arg.subId, true);
         }
 
         /**
@@ -12959,28 +13261,43 @@
          * or SatelliteController.
          * TODO (b/267826133) we need to do this for all subscriptions on the device.
          */
-        registerForSatelliteProvisionStateChanged(subId, null, null);
+        registerForSatelliteProvisionStateChangedInternal(arg.subId, null, null);
     }
 
-    private void handleCmdCancelProvisionSatelliteService(int subId, Message onCompleted) {
-        final Phone phone = getPhoneOrDefault(
-                subId, "handleCmdCancelProvisionSatelliteService");
-        if (phone == null) {
+    private void handleCmdDeprovisionSatelliteService(
+            @NonNull ProvisionSatelliteServiceArgument arg,
+            @NonNull Phone phone, Message onCompleted) {
+        if (arg == null) {
+            loge("handleCmdDeprovisionSatelliteService: arg is null");
             return;
         }
-        phone.cancelProvisionSatelliteService(onCompleted, getSatelliteImsi(subId));
+        if (phone == null) {
+            loge("handleCmdDeprovisionSatelliteService: phone is null");
+            if (arg.callback != null) {
+                arg.callback.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            }
+            return;
+        }
+        phone.deprovisionSatelliteService(onCompleted, arg.token);
     }
 
-    private String getSatelliteImsi(int subId) {
-        if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
-            return "";
+    private void handleEventDeprovisionSatelliteServiceDone(
+            @NonNull ProvisionSatelliteServiceArgument arg,
+            @SatelliteManager.SatelliteError int result) {
+        if (arg == null) {
+            loge("handleEventDeprovisionSatelliteServiceDone: arg is null");
+            return;
+        }
+        log("handleEventDeprovisionSatelliteServiceDone: result="
+                + result + ", subId=" + arg.subId);
+
+        if (arg.callback != null) {
+            arg.callback.accept(result);
         }
 
-        if (mSubscriptionController == null) {
-            loge("getSatelliteImsi: mSubscriptionController is null");
-            return "";
+        if (result == SatelliteManager.SATELLITE_ERROR_NONE) {
+            setSatelliteProvisioned(arg.subId, false);
         }
-        return mSubscriptionController.getImsiPrivileged(subId);
     }
 
     private Phone getPhoneOrDefault(int subId, String caller) {
@@ -13028,7 +13345,7 @@
      * {@link SubscriptionController} when setting satellite enabled for an active subscription.
      * Otherwise, {@link android.Manifest.permission#SATELLITE_COMMUNICATION} will be enforced.
      */
-    private void setSatelliteProvisioned(int subId, boolean isEnabled) {
+    private synchronized void setSatelliteProvisioned(int subId, boolean isEnabled) {
         if (subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
             if (mSubscriptionController == null) {
                 loge("setSatelliteProvisioned mSubscriptionController is null");
@@ -13113,6 +13430,54 @@
     }
 
     /**
+     * Register for the satellite provision state change.
+     *
+     * @param subId The subId of the subscription associated with the satellite service.
+     * @param errorCallback The callback to get the error code of the request.
+     * @param callback The callback to handle the satellite provision state changed event.
+     */
+    private void registerForSatelliteProvisionStateChangedInternal(int subId,
+            @Nullable IIntegerConsumer errorCallback, @Nullable ISatelliteStateListener callback) {
+        Consumer<Integer> result = null;
+        if (errorCallback != null) {
+            result = FunctionalUtils.ignoreRemoteException(errorCallback::accept);
+        }
+        if (!isSatelliteSupported()) {
+            if (result != null) {
+                result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+            }
+            return;
+        }
+
+        final int validSubId = getValidSatelliteSubId(subId);
+        Phone phone = getPhoneOrDefault(validSubId, "registerForSatelliteProvisionStateChanged");
+        if (phone == null) {
+            if (result != null) {
+                result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            }
+            return;
+        }
+
+        SatelliteProvisionStateChangedHandler satelliteProvisionStateChangedHandler =
+                mSatelliteProvisionStateChangedHandlers.get(validSubId);
+        if (satelliteProvisionStateChangedHandler == null) {
+            satelliteProvisionStateChangedHandler = new SatelliteProvisionStateChangedHandler(
+                    Looper.getMainLooper(), validSubId);
+            phone.registerForSatelliteProvisionStateChanged(satelliteProvisionStateChangedHandler,
+                    SatelliteProvisionStateChangedHandler.EVENT_PROVISION_STATE_CHANGED, null);
+        }
+
+        if (callback != null) {
+            satelliteProvisionStateChangedHandler.addListener(callback);
+        }
+        mSatelliteProvisionStateChangedHandlers.put(
+                validSubId, satelliteProvisionStateChangedHandler);
+        if (result != null) {
+            result.accept(SatelliteManager.SATELLITE_ERROR_NONE);
+        }
+    }
+
+    /**
      * Check whether the caller (or self, if not processing an IPC) can read device identifiers.
      *
      * <p>This method behaves in one of the following ways:
@@ -13163,4 +13528,14 @@
             return mCarrierId;
         }
     }
+
+    private static SatelliteDatagram[] convertToSatelliteDatagramArray(byte[][] datagrams) {
+        // Convert 2D byte array into SatelliteDatagramArray.
+        SatelliteDatagram[] satelliteDatagramArray =
+                new SatelliteDatagram[datagrams.length];
+        for (int i = 0; i < datagrams.length; i++) {
+            satelliteDatagramArray[i] = new SatelliteDatagram(datagrams[i]);
+        }
+        return satelliteDatagramArray;
+    }
 }