Merge "Do CSFB instead of scanning with CS preferred network list"
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 5ec4f65..901c8b2 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -145,10 +145,10 @@
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.stub.ImsConfigImplBase;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.telephony.satellite.ISatelliteCapabilitiesConsumer;
 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;
@@ -175,7 +175,6 @@
 import com.android.internal.telephony.IBooleanConsumer;
 import com.android.internal.telephony.ICallForwardingInfoCallback;
 import com.android.internal.telephony.IImsStateCallback;
-import com.android.internal.telephony.IIntArrayConsumer;
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.INumberVerificationCallback;
 import com.android.internal.telephony.ITelephony;
@@ -394,18 +393,24 @@
     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_GET_PROVISIONED_SATELLITE_FEATURES = 129;
-    private static final int EVENT_GET_PROVISIONED_SATELLITE_FEATURES_DONE = 130;
-    private static final int CMD_SET_SATELLITE_POWER = 131;
-    private static final int EVENT_SET_SATELLITE_POWER_DONE = 132;
-    private static final int CMD_IS_SATELLITE_POWER_ON = 133;
-    private static final int EVENT_IS_SATELLITE_POWER_ON_DONE = 134;
-    private static final int CMD_IS_SATELLITE_SUPPORTED = 135;
-    private static final int EVENT_IS_SATELLITE_SUPPORTED_DONE = 136;
-    private static final int CMD_GET_SATELLITE_CAPABILITIES = 137;
-    private static final int EVENT_GET_SATELLITE_CAPABILITIES_DONE = 138;
+    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;
+    private static final int EVENT_IS_SATELLITE_ENABLED_DONE = 132;
+    private static final int CMD_IS_SATELLITE_SUPPORTED = 133;
+    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;
+    private static final int CMD_SEND_SATELLITE_DATAGRAM = 139;
+    private static final int EVENT_SEND_SATELLITE_DATAGRAM_DONE = 140;
+    private static final int CMD_IS_SATELLITE_COMMUNICATION_ALLOWED = 141;
+    private static final int EVENT_IS_SATELLITE_COMMUNICATION_ALLOWED_DONE = 142;
+    private static final int CMD_GET_TIME_SATELLITE_NEXT_VISIBLE = 143;
+    private static final int EVENT_GET_TIME_SATELLITE_NEXT_VISIBLE_DONE = 144;
     // Parameters of select command.
     private static final int SELECT_COMMAND = 0xA4;
     private static final int SELECT_P1 = 0x04;
@@ -436,20 +441,39 @@
     private static final int USER_ACTIVITY_NOTIFICATION_DELAY = 200;
 
     private Set<Integer> mCarrierPrivilegeTestOverrideSubIds = new ArraySet<>();
-    private ConcurrentHashMap<IBinder, SatellitePositionUpdateHandler>
-            mSatellitePositionUpdateHandlers = new ConcurrentHashMap<>();
+
     /**
      * Map key: subId, value: callback to get error code of the provision request.
      */
-    private ConcurrentHashMap<Integer, IIntegerConsumer> mSatelliteProvisionCallbacks =
+    private final ConcurrentHashMap<Integer, Consumer<Integer>> mSatelliteProvisionCallbacks =
             new ConcurrentHashMap<>();
-
+    /**
+     * Map key: subId, value: SatellitePositionUpdateHandler to notify registrants.
+     */
+    private final ConcurrentHashMap<IBinder, SatellitePositionUpdateHandler>
+            mSatellitePositionUpdateHandlers = new ConcurrentHashMap<>();
     /**
      * Map key: subId, value: SatelliteProvisionStateChangedHandler to notify registrants.
      */
-    private ConcurrentHashMap<Integer, SatelliteProvisionStateChangedHandler>
+    private final ConcurrentHashMap<Integer, SatelliteProvisionStateChangedHandler>
             mSatelliteProvisionStateChangedHandlers = new ConcurrentHashMap<>();
 
+    private Boolean mIsSatelliteSupported = null;
+    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_";
@@ -545,17 +569,30 @@
     }
 
     private static final class ProvisionSatelliteServiceArgument {
-        public @NonNull int[] features;
-        public @NonNull IIntegerConsumer callback;
+        public @NonNull String token;
+        public @NonNull Consumer<Integer> callback;
         public int subId;
 
-        ProvisionSatelliteServiceArgument(int[] features, IIntegerConsumer callback, int subId) {
-            this.features = features;
+        ProvisionSatelliteServiceArgument(String token, Consumer<Integer> callback, int subId) {
+            this.token = token;
             this.callback = callback;
             this.subId = subId;
         }
     }
 
+    private static final class SendSatelliteDatagramArgument {
+        public @SatelliteManager.DatagramType int datagramType;
+        public @NonNull SatelliteDatagram datagram;
+        public @NonNull Consumer<Integer> callback;
+
+        SendSatelliteDatagramArgument(@SatelliteManager.DatagramType int datagramType,
+                SatelliteDatagram datagram, Consumer<Integer> callback) {
+            this.datagramType = datagramType;
+            this.datagram = datagram;
+            this.callback = callback;
+        }
+    }
+
     private static final class SatellitePositionUpdateHandler extends Handler {
         public static final int EVENT_POSITION_UPDATE = 1;
         public static final int EVENT_MESSAGE_TRANSFER_STATE_UPDATE = 2;
@@ -584,7 +621,8 @@
                     AsyncResult ar = (AsyncResult) msg.obj;
                     int state = (int) ar.result;
                     try {
-                        mCallback.onMessageTransferStateUpdate(state);
+                        // TODO: get correct responses back from indication
+                        mCallback.onMessageTransferStateUpdate(state, 0, 0, 0);
                     } catch (RemoteException e) {
                         loge("EVENT_MESSAGE_TRANSFER_STATE_UPDATE RemoteException: " + e);
                     }
@@ -622,19 +660,17 @@
                 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);
                         }
                     });
 
-                    setSatelliteEnabled(provisioned);
+                    setSatelliteProvisioned(provisioned);
                     /**
                      * TODO: Take bugreport if provisioned is true and user did not initiate the
                      * provision procedure for the corresponding subscription.
@@ -646,12 +682,115 @@
             }
         }
 
-        private void setSatelliteEnabled(boolean isEnabled) {
+        private void setSatelliteProvisioned(boolean isProvisioned) {
             if (mSubId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
                 SubscriptionManager.setSubscriptionProperty(
-                        mSubId, SubscriptionManager.SATELLITE_ENABLED, isEnabled ? "1" : "0");
+                        mSubId, SubscriptionManager.SATELLITE_ENABLED, isProvisioned ? "1" : "0");
             } else {
-                //TODO (b/267826133): set enabled via SatelliteController.
+                //TODO (b/267826133): set via SatelliteController.
+            }
+        }
+    }
+
+    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);
             }
         }
     }
@@ -1663,13 +1802,14 @@
                     break;
                 }
 
-                case CMD_SET_ALLOWED_CARRIERS:
+                case CMD_SET_ALLOWED_CARRIERS: {
                     request = (MainThreadRequest) msg.obj;
                     CarrierRestrictionRules argument =
                             (CarrierRestrictionRules) request.argument;
                     onCompleted = obtainMessage(EVENT_SET_ALLOWED_CARRIERS_DONE, request);
                     defaultPhone.setAllowedCarriers(argument, onCompleted, request.workSource);
                     break;
+                }
 
                 case EVENT_SET_ALLOWED_CARRIERS_DONE:
                     ar = (AsyncResult) msg.obj;
@@ -1957,7 +2097,7 @@
                 case EVENT_CMD_MODEM_REBOOT_DONE:
                     handleNullReturnEvent(msg, "rebootModem");
                     break;
-                case CMD_REQUEST_ENABLE_MODEM:
+                case CMD_REQUEST_ENABLE_MODEM: {
                     request = (MainThreadRequest) msg.obj;
                     boolean enable = (boolean) request.argument;
                     onCompleted = obtainMessage(EVENT_ENABLE_MODEM_DONE, request);
@@ -1965,6 +2105,7 @@
                     PhoneConfigurationManager.getInstance()
                             .enablePhone(request.phone, enable, onCompleted);
                     break;
+                }
                 case EVENT_ENABLE_MODEM_DONE: {
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
@@ -2411,9 +2552,8 @@
                         phone.startSatellitePositionUpdates(onCompleted);
                     } else {
                         loge("startSatellitePositionUpdates: No phone object");
-                        request.result =
-                                SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
-                        notifyRequester(request);
+                        ((Consumer<Integer>) request.argument).accept(
+                                SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
                     }
                     break;
                 }
@@ -2421,20 +2561,8 @@
                 case EVENT_START_SATELLITE_POSITION_UPDATES_DONE: {
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
-                    if (ar.exception == null) {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_SUCCESS;
-                    } else {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_ERROR;
-                        if (ar.exception instanceof CommandException) {
-                            CommandException.Error error =
-                                    ((CommandException) (ar.exception)).getCommandError();
-                            request.result = RILUtils.convertToSatelliteError(error);
-                            loge("startSatellitePositionUpdates CommandException: " + ar.exception);
-                        } else {
-                            loge("startSatellitePositionUpdates unknown exception:" + ar.exception);
-                        }
-                    }
-                    notifyRequester(request);
+                    int error = getSatelliteError(ar, "startSatellitePositionUpdates", false);
+                    ((Consumer<Integer>) request.argument).accept(error);
                     break;
                 }
 
@@ -2447,9 +2575,8 @@
                         phone.stopSatellitePositionUpdates(onCompleted);
                     } else {
                         loge("stopSatellitePositionUpdates: No phone object");
-                        request.result =
-                                SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
-                        notifyRequester(request);
+                        ((Consumer<Integer>) request.argument).accept(
+                                SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
                     }
                     break;
                 }
@@ -2457,20 +2584,8 @@
                 case EVENT_STOP_SATELLITE_POSITION_UPDATES_DONE: {
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
-                    if (ar.exception == null) {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_SUCCESS;
-                    } else {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_ERROR;
-                        if (ar.exception instanceof CommandException) {
-                            CommandException.Error error =
-                                    ((CommandException) (ar.exception)).getCommandError();
-                            request.result = RILUtils.convertToSatelliteError(error);
-                            loge("stopSatellitePositionUpdates CommandException: " + ar.exception);
-                        } else {
-                            loge("stopSatellitePositionUpdates unknown exception:" + ar.exception);
-                        }
-                    }
-                    notifyRequester(request);
+                    int error = getSatelliteError(ar, "stopSatellitePositionUpdates", false);
+                    ((Consumer<Integer>) request.argument).accept(error);
                     break;
                 }
 
@@ -2483,9 +2598,8 @@
                         phone.getMaxCharactersPerSatelliteTextMessage(onCompleted);
                     } else {
                         loge("getMaxCharactersPerSatelliteTextMessage: No phone object");
-                        request.result = SatelliteManager
-                                .SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
-                        notifyRequester(request);
+                        ((ResultReceiver) request.argument).send(
+                                SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
                     }
                     break;
                 }
@@ -2493,46 +2607,31 @@
                 case EVENT_GET_MAX_CHAR_PER_SATELLITE_TEXT_MSG_DONE: {
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
-                    Consumer<Integer> callback = (Consumer<Integer>) request.argument;
-                    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("getMaxCharactersPerSatelliteTextMessage: "
-                                    + "CommandException: " + ar.exception);
-                        } else {
-                            loge("getMaxCharactersPerSatelliteTextMessage: "
-                                    + "unknown exception:" + ar.exception);
-                        }
-                    } else if (ar.result == null) {
-                        request.result = SatelliteManager
-                                .SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
-                        loge("getMaxCharactersPerSatelliteTextMessage: result is null");
-                    } else {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_SUCCESS;
+                    int error =
+                            getSatelliteError(ar, "getMaxCharactersPerSatelliteTextMessage", true);
+                    Bundle bundle = new Bundle();
+                    if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
                         int maxCharLimit = ((int[]) ar.result)[0];
                         if (DBG) log("getMaxCharactersPerSatelliteTextMessage: " + maxCharLimit);
-                        callback.accept(maxCharLimit);
+                        bundle.putInt(SatelliteManager.KEY_MAX_CHARACTERS_PER_SATELLITE_TEXT,
+                                maxCharLimit);
                     }
-                    notifyRequester(request);
+                    ((ResultReceiver) request.argument).send(error, bundle);
                     break;
                 }
 
                 case CMD_PROVISION_SATELLITE_SERVICE: {
                     request = (MainThreadRequest) msg.obj;
-                    onCompleted =
-                            obtainMessage(EVENT_PROVISION_SATELLITE_SERVICE_DONE, request);
+                    onCompleted = obtainMessage(EVENT_PROVISION_SATELLITE_SERVICE_DONE, request);
                     Phone phone = getPhoneFromRequest(request);
+                    ProvisionSatelliteServiceArgument argument =
+                            (ProvisionSatelliteServiceArgument) request.argument;
                     if (phone != null) {
-                        handleCmdProvisionSatelliteService(
-                                (ProvisionSatelliteServiceArgument) request.argument,
-                                phone, onCompleted);
+                        handleCmdProvisionSatelliteService(argument, phone, onCompleted);
                     } else {
                         loge("provisionSatelliteService: No phone object");
-                        request.result =
-                                SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
+                        argument.callback.accept(
+                                SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
                         notifyRequester(request);
                     }
                     break;
@@ -2541,187 +2640,94 @@
                 case EVENT_PROVISION_SATELLITE_SERVICE_DONE: {
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
-                    if (ar.exception == null) {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_SUCCESS;
-                    } else {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_ERROR;
-                        if (ar.exception instanceof CommandException) {
-                            CommandException.Error error =
-                                    ((CommandException) (ar.exception)).getCommandError();
-                            request.result = RILUtils.convertToSatelliteError(error);
-                            loge("provisionSatelliteService CommandException: " + ar.exception);
-                        } else {
-                            loge("provisionSatelliteService unknown exception:" + ar.exception);
-                        }
-                    }
-                    handleEventProvisionSatelliteServiceDone(request);
+                    int errorCode = getSatelliteError(ar, "provisionSatelliteService", false);
+                    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);
-                    break;
-                }
-
-                case EVENT_CANCEL_PROVISION_SATELLITE_SERVICE_DONE: {
-                    ar = (AsyncResult) msg.obj;
-                    request = (MainThreadRequest) ar.userObj;
-
-                    if (ar.exception == null) {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_SUCCESS;
-                        log("cancelProvisionSatelliteService succeeded for subId="
-                                + (int) request.argument);
-                    } else {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_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);
-                        }
-                    }
-                    notifyRequester(request);
-                    break;
-                }
-
-                case CMD_GET_PROVISIONED_SATELLITE_FEATURES: {
-                    request = (MainThreadRequest) msg.obj;
-                    onCompleted = obtainMessage(EVENT_GET_PROVISIONED_SATELLITE_FEATURES_DONE,
-                            request);
+                    onCompleted = obtainMessage(EVENT_DEPROVISION_SATELLITE_SERVICE_DONE, request);
                     Phone phone = getPhoneFromRequest(request);
+                    ProvisionSatelliteServiceArgument argument =
+                            (ProvisionSatelliteServiceArgument) request.argument;
                     if (phone != null) {
-                        phone.getProvisionedSatelliteFeatures(onCompleted);
+                        handleCmdDeprovisionSatelliteService(argument, phone, onCompleted);
                     } else {
-                        loge("getProvisionedSatelliteFeatures: No phone object");
-                        request.result = SatelliteManager
-                                .SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
+                        loge("deprovisionSatelliteService: No phone object");
+                        if (argument.callback != null) {
+                            argument.callback.accept(
+                                    SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                        }
                         notifyRequester(request);
                     }
                     break;
                 }
 
-                case EVENT_GET_PROVISIONED_SATELLITE_FEATURES_DONE: {
+                case EVENT_DEPROVISION_SATELLITE_SERVICE_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("getProvisionedSatelliteFeatures: "
-                                    + "CommandException: " + ar.exception);
-                        } else {
-                            loge("getProvisionedSatelliteFeatures: "
-                                    + "unknown exception:" + ar.exception);
-                        }
-                    } else if (ar.result == null) {
-                        request.result = SatelliteManager
-                                .SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
-                        loge("getProvisionedSatelliteFeatures: result is null");
-                    } else {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_SUCCESS;
-                        int[] features = ((int[]) ar.result);
-                        if (DBG) {
-                            log("getProvisionedSatelliteFeatures features:"
-                                    + Arrays.toString(features));
-                        }
-                        IIntArrayConsumer callback = (IIntArrayConsumer) request.argument;
-                        if (callback != null) {
-                            try {
-                                callback.accept(features);
-                            } catch (RemoteException ex) {
-                                log("getProvisionedSatelliteFeatures: remote callback"
-                                        + " not available");
-                            }
-                        } else {
-                            log("getProvisionedSatelliteFeatures: callback is null");
-                        }
-                    }
+                    int errorCode = getSatelliteError(ar, "deprovisionSatelliteService", false);
+                    handleEventDeprovisionSatelliteServiceDone(
+                            (ProvisionSatelliteServiceArgument) request.argument, errorCode);
                     notifyRequester(request);
                     break;
                 }
 
-                case CMD_SET_SATELLITE_POWER: {
+                case CMD_SET_SATELLITE_ENABLED: {
                     request = (MainThreadRequest) msg.obj;
-                    onCompleted = obtainMessage(EVENT_SET_SATELLITE_POWER_DONE, request);
+                    onCompleted = obtainMessage(EVENT_SET_SATELLITE_ENABLED_DONE, request);
+                    Pair<Boolean, Consumer<Integer>> argument =
+                            (Pair<Boolean, Consumer<Integer>>) request.argument;
                     Phone phone = getPhoneFromRequest(request);
                     if (phone != null) {
-                        phone.setSatellitePower(onCompleted, (boolean) request.argument);
+                        boolean enable = argument.first.booleanValue();
+                        phone.setSatellitePower(onCompleted, enable);
                     } else {
-                        loge("setSatellitePower: No phone object");
-                        request.result = SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
+                        loge("setSatelliteEnabled: No phone object");
+                        argument.second.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
                         notifyRequester(request);
                     }
                     break;
                 }
 
-                case EVENT_SET_SATELLITE_POWER_DONE: {
+                case EVENT_SET_SATELLITE_ENABLED_DONE: {
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
-                    if (ar.exception == null) {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_SUCCESS;
-                    } else {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_ERROR;
-                        if (ar.exception instanceof CommandException) {
-                            CommandException.Error error =
-                                    ((CommandException) (ar.exception)).getCommandError();
-                            request.result = RILUtils.convertToSatelliteError(error);
-                            loge("setSatellitePower CommandException: " + ar.exception);
-                        } else {
-                            loge("setSatellitePower unknown exception:" + ar.exception);
-                        }
-                    }
-                    notifyRequester(request);
+                    Pair<Boolean, Consumer<Integer>> argument =
+                            (Pair<Boolean, Consumer<Integer>>) request.argument;
+                    int error = getSatelliteError(ar, "setSatelliteEnabled", false);
+                    argument.second.accept(error);
                     break;
                 }
 
-                case CMD_IS_SATELLITE_POWER_ON: {
+                case CMD_IS_SATELLITE_ENABLED: {
                     request = (MainThreadRequest) msg.obj;
-                    onCompleted = obtainMessage(EVENT_IS_SATELLITE_POWER_ON_DONE, request);
+                    onCompleted = obtainMessage(EVENT_IS_SATELLITE_ENABLED_DONE, request);
                     Phone phone = getPhoneFromRequest(request);
                     if (phone != null) {
                         phone.isSatellitePowerOn(onCompleted);
                     } else {
-                        loge("isSatellitePowerOn: No phone object");
-                        request.result = SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
-                        notifyRequester(request);
+                        loge("isSatelliteEnabled: No phone object");
+                        ((ResultReceiver) request.argument).send(
+                                SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
                     }
                     break;
                 }
 
-                case EVENT_IS_SATELLITE_POWER_ON_DONE: {
+                case EVENT_IS_SATELLITE_ENABLED_DONE: {
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
-                    Consumer<Boolean> callback = (Consumer<Boolean>) request.argument;
-                    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("isSatellitePowerOn CommandException: " + ar.exception);
-                        } else {
-                            loge("isSatellitePowerOn unknown exception:" + ar.exception);
-                        }
-                    } else if (ar.result == null) {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
-                        loge("isSatellitePowerOn: result is null");
-                    } else {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_SUCCESS;
-                        boolean powerOn = ((int[]) ar.result)[0] == 1;
-                        if (DBG) log("isSatellitePowerOn: " + powerOn);
-                        callback.accept(powerOn);
+                    int error = getSatelliteError(ar, "isSatelliteEnabled", true);
+                    Bundle bundle = new Bundle();
+                    if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+                        boolean enabled = ((int[]) ar.result)[0] == 1;
+                        if (DBG) log("isSatelliteEnabled: " + enabled);
+                        bundle.putBoolean(SatelliteManager.KEY_SATELLITE_ENABLED, enabled);
                     }
-                    notifyRequester(request);
+                    ((ResultReceiver) request.argument).send(error, bundle);
                     break;
                 }
 
@@ -2733,8 +2739,8 @@
                         phone.isSatelliteSupported(onCompleted);
                     } else {
                         loge("isSatelliteSupported: No phone object");
-                        request.result = SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
-                        notifyRequester(request);
+                        ((ResultReceiver) request.argument).send(
+                                SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
                     }
                     break;
                 }
@@ -2742,27 +2748,21 @@
                 case EVENT_IS_SATELLITE_SUPPORTED_DONE: {
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
-                    Consumer<Boolean> callback = (Consumer<Boolean>) request.argument;
-                    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("isSatelliteSupported CommandException: " + ar.exception);
-                        } else {
-                            loge("isSatelliteSupported unknown exception:" + ar.exception);
+                    int error = getSatelliteError(ar, "isSatelliteSupported", true);
+                    Bundle bundle = new Bundle();
+                    if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+                        boolean supported = ((int[]) ar.result)[0] == 1;
+                        if (DBG) log("isSatelliteSupported: " + supported);
+                        bundle.putBoolean(SatelliteManager.KEY_SATELLITE_SUPPORTED, supported);
+                        synchronized (mIsSatelliteSupportedLock) {
+                            mIsSatelliteSupported = supported;
                         }
-                    } else if (ar.result == null) {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
-                        loge("isSatelliteSupported: result is null");
                     } else {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_SUCCESS;
-                        boolean powerOn = ((int[]) ar.result)[0] == 1;
-                        if (DBG) log("isSatelliteSupported: " + powerOn);
-                        callback.accept(powerOn);
+                        synchronized (mIsSatelliteSupportedLock) {
+                            mIsSatelliteSupported = null;
+                        }
                     }
-                    notifyRequester(request);
+                    ((ResultReceiver) request.argument).send(error, bundle);
                     break;
                 }
 
@@ -2774,8 +2774,8 @@
                         phone.getSatelliteCapabilities(onCompleted);
                     } else {
                         loge("getSatelliteCapabilities: No phone object");
-                        request.result = SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
-                        notifyRequester(request);
+                        ((ResultReceiver) request.argument).send(
+                                SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
                     }
                     break;
                 }
@@ -2783,31 +2783,155 @@
                 case EVENT_GET_SATELLITE_CAPABILITIES_DONE: {
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
-                    Consumer<SatelliteCapabilities> callback =
-                            (Consumer<SatelliteCapabilities>) request.argument;
+                    int error = getSatelliteError(ar, "getSatelliteCapabilities", true);
+                    Bundle bundle = new Bundle();
+                    if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+                        SatelliteCapabilities capabilities = (SatelliteCapabilities) ar.result;
+                        if (DBG) log("getSatelliteCapabilities: " + capabilities);
+                        bundle.putParcelable(SatelliteManager.KEY_SATELLITE_CAPABILITIES,
+                                capabilities);
+                    }
+                    ((ResultReceiver) request.argument).send(error, bundle);
+                    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("getSatelliteCapabilities CommandException: " + ar.exception);
+                            loge("pollPendingSatelliteDatagrams: "
+                                    + "CommandException: " + ar.exception);
                         } else {
-                            loge("getSatelliteCapabilities unknown exception:" + ar.exception);
+                            loge("pollPendingSatelliteDatagrams: "
+                                    + "unknown exception:" + ar.exception);
                         }
                     } else if (ar.result == null) {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
-                        loge("getSatelliteCapabilities: result is null");
+                        request.result = SatelliteManager
+                                .SATELLITE_INVALID_TELEPHONY_STATE;
+                        loge("pollPendingSatelliteDatagrams: result is null");
                     } else {
-                        request.result = SatelliteManager.SATELLITE_SERVICE_SUCCESS;
-                        SatelliteCapabilities capabilities = (SatelliteCapabilities) ar.result;
-                        if (DBG) log("getSatelliteCapabilities: " + capabilities);
-                        callback.accept(capabilities);
+                        request.result = SatelliteManager.SATELLITE_ERROR_NONE;
                     }
                     notifyRequester(request);
                     break;
                 }
 
+                case CMD_SEND_SATELLITE_DATAGRAM: {
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted =
+                            obtainMessage(EVENT_SEND_SATELLITE_DATAGRAM_DONE, request);
+                    Phone phone = getPhoneFromRequest(request);
+                    SendSatelliteDatagramArgument argument =
+                            (SendSatelliteDatagramArgument) request.argument;
+                    if (phone != null) {
+                        phone.sendSatelliteDatagram(onCompleted, argument.datagram);
+                    } else {
+                        loge("sendSatelliteDatagram: No phone object");
+                        argument.callback
+                                .accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+                    }
+                    break;
+                }
+
+                case EVENT_SEND_SATELLITE_DATAGRAM_DONE: {
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    int error = getSatelliteError(ar, "sendSatelliteDatagram",
+                            false);
+                    SendSatelliteDatagramArgument argument =
+                            (SendSatelliteDatagramArgument) request.argument;
+                    argument.callback.accept(error);
+                    break;
+                }
+
+                case CMD_IS_SATELLITE_COMMUNICATION_ALLOWED: {
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_IS_SATELLITE_COMMUNICATION_ALLOWED_DONE,
+                            request);
+                    Phone phone = getPhoneFromRequest(request);
+                    if (phone != null) {
+                        phone.isSatelliteCommunicationAllowedForCurrentLocation(onCompleted);
+                    } else {
+                        loge("isSatelliteCommunicationAllowedForCurrentLocation: No phone object");
+                        ((ResultReceiver) request.argument).send(
+                                SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+                    }
+                    break;
+                }
+
+                case EVENT_IS_SATELLITE_COMMUNICATION_ALLOWED_DONE: {
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    int error = getSatelliteError(
+                            ar, "isSatelliteCommunicationAllowedForCurrentLocation", true);
+                    Bundle bundle = new Bundle();
+                    if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+                        boolean communicationAllowed = ((int[]) ar.result)[0] == 1;
+                        if (DBG) {
+                            log("isSatelliteCommunicationAllowedForCurrentLocation: "
+                                    + communicationAllowed);
+                        }
+                        bundle.putBoolean(SatelliteManager.KEY_SATELLITE_COMMUNICATION_ALLOWED,
+                                communicationAllowed);
+                    }
+                    ((ResultReceiver) request.argument).send(error, bundle);
+                    break;
+                }
+
+                case CMD_GET_TIME_SATELLITE_NEXT_VISIBLE: {
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted = obtainMessage(EVENT_GET_TIME_SATELLITE_NEXT_VISIBLE_DONE,
+                            request);
+                    Phone phone = getPhoneFromRequest(request);
+                    if (phone != null) {
+                        phone.requestTimeForNextSatelliteVisibility(onCompleted);
+                    } else {
+                        loge("requestTimeForNextSatelliteVisibility: No phone object");
+                        ((ResultReceiver) request.argument).send(
+                                SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+                    }
+                    break;
+                }
+
+                case EVENT_GET_TIME_SATELLITE_NEXT_VISIBLE_DONE: {
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    int error =
+                            getSatelliteError(ar, "requestTimeForNextSatelliteVisibility", true);
+                    Bundle bundle = new Bundle();
+                    if (error == SatelliteManager.SATELLITE_ERROR_NONE) {
+                        int nextVisibilityDuration = ((int[]) ar.result)[0];
+                        if (DBG) {
+                            log("requestTimeForNextSatelliteVisibility: " + nextVisibilityDuration);
+                        }
+                        bundle.putInt(SatelliteManager.KEY_SATELLITE_NEXT_VISIBILITY,
+                                nextVisibilityDuration);
+                    }
+                    ((ResultReceiver) request.argument).send(error, bundle);
+                    break;
+                }
+
                 default:
                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
                     break;
@@ -3020,6 +3144,24 @@
         PropertyInvalidatedCache.invalidateCache(TelephonyManager.CACHE_KEY_PHONE_ACCOUNT_TO_SUBID);
         publish();
         CarrierAllowListInfo.loadInstance(mApp);
+        mSatelliteSupportedReceiver = new ResultReceiver(mMainThreadHandler) {
+            @Override
+            protected void onReceiveResult(int resultCode, Bundle resultData) {
+                if (resultCode == SatelliteManager.SATELLITE_ERROR_NONE
+                        && resultData.containsKey(SatelliteManager.KEY_SATELLITE_SUPPORTED)) {
+                    synchronized (mIsSatelliteSupportedLock) {
+                        mIsSatelliteSupported = resultData.getBoolean(
+                                SatelliteManager.KEY_SATELLITE_SUPPORTED);
+                    }
+                } else {
+                    synchronized (mIsSatelliteSupportedLock) {
+                        mIsSatelliteSupported = null;
+                    }
+                }
+            }
+        };
+        requestIsSatelliteSupported(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+                mSatelliteSupportedReceiver);
     }
 
     @VisibleForTesting
@@ -6041,7 +6183,7 @@
 
     private boolean isActiveSubscription(int subId) {
         if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
-            return SubscriptionManagerService.getInstance().isActiveSubId(subId,
+            return getSubscriptionManagerService().isActiveSubId(subId,
                     mApp.getOpPackageName(), mApp.getFeatureId());
         }
         return mSubscriptionController.isActiveSubId(subId);
@@ -8007,7 +8149,7 @@
 
             ParcelUuid groupUuid;
             if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
-                final SubscriptionInfo info = SubscriptionManagerService.getInstance()
+                final SubscriptionInfo info = getSubscriptionManagerService()
                         .getSubscriptionInfo(subId);
                 groupUuid = info.getGroupUuid();
             } else {
@@ -8024,7 +8166,7 @@
             final List<String> mergedSubscriberIds = new ArrayList<>();
             List<SubscriptionInfo> groupInfos;
             if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
-                groupInfos = SubscriptionManagerService.getInstance()
+                groupInfos = getSubscriptionManagerService()
                         .getSubscriptionsInGroup(groupUuid, mApp.getOpPackageName(),
                                 mApp.getAttributionTag());
             } else {
@@ -8594,7 +8736,7 @@
         try {
             SubscriptionInfo info;
             if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
-                info = SubscriptionManagerService.getInstance().getActiveSubscriptionInfo(subId,
+                info = getSubscriptionManagerService().getActiveSubscriptionInfo(subId,
                         phone.getContext().getOpPackageName(),
                         phone.getContext().getAttributionTag());
                 if (info == null) {
@@ -8661,7 +8803,7 @@
      */
     private List<SubscriptionInfo> getActiveSubscriptionInfoListPrivileged() {
         if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
-            return SubscriptionManagerService.getInstance().getActiveSubscriptionInfoList(
+            return getSubscriptionManagerService().getActiveSubscriptionInfoList(
                     mApp.getOpPackageName(), mApp.getAttributionTag());
         }
         return mSubscriptionController.getActiveSubscriptionInfoList(mApp.getOpPackageName(),
@@ -8876,7 +9018,7 @@
         try {
             // isActiveSubId requires READ_PHONE_STATE, which we already check for above
             if (PhoneFactory.isSubscriptionManagerServiceEnabled()) {
-                SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance()
+                SubscriptionInfoInternal subInfo = getSubscriptionManagerService()
                         .getSubscriptionInfoInternal(subId);
                 if (subInfo == null || !subInfo.isActive()) {
                     Rlog.d(LOG_TAG, "getServiceStateForSubscriber returning null for inactive "
@@ -12619,120 +12761,127 @@
     }
 
     /**
-     * Power on or off the satellite modem.
+     * Enable or disable the satellite modem. If the satellite modem is enabled, this will also
+     * disable the cellular modem, and if the satellite modem is disabled, this will also re-enable
+     * the cellular modem.
      *
-     * @param subId The subId to set satellite power for.
-     * @param powerOn {@code true} to power on the satellite modem and {@code false} to power off.
-     * @return The result of the operation.
+     * @param subId The subId of the subscription to set satellite enabled for.
+     * @param enable {@code true} to enable the satellite modem and {@code false} to disable.
+     * @param callback The callback to get the error code of the request.
      *
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
-    @SatelliteManager.SatelliteServiceResult public int setSatellitePower(int subId,
-            boolean powerOn) {
-        enforceSatelliteCommunicationPermission("setSatellitePower");
+    public void setSatelliteEnabled(int subId, boolean enable, @NonNull IIntegerConsumer callback) {
+        enforceSatelliteCommunicationPermission("setSatelliteEnabled");
+        Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
+        if (!isSatelliteSupported()) {
+            result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+            return;
+        }
 
         final int validSubId = getValidSatelliteSubId(subId);
-        if (!isSatelliteEnabled(validSubId)) {
-            return SatelliteManager.SATELLITE_SERVICE_DISABLED;
+        if (!isSatelliteProvisioned(validSubId)) {
+            result.accept(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED);
+            return;
         }
 
-        Phone phone = getPhoneOrDefault(validSubId, "setSatellitePower");
+        Phone phone = getPhoneOrDefault(validSubId, "setSatelliteEnabled");
         if (phone == null) {
-            return SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
+            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            return;
         }
 
-        int result = (int) sendRequest(CMD_SET_SATELLITE_POWER, powerOn, subId);
-        if (DBG) log("setSatellitePower result: " + result);
-        return result;
+        Pair<Boolean, Consumer<Integer>> arg = new Pair<>(enable, result);
+        sendRequestAsync(CMD_SET_SATELLITE_ENABLED, arg, phone, null);
     }
 
     /**
-     * Check whether the satellite modem is powered on.
+     * Request to get whether the satellite modem is enabled.
      *
-     * @param subId The subId to check satellite power for.
-     * @param callback The callback that will be used to send the result if the operation is
-     *                 successful. Returns {@code true} if the satellite modem is powered on and
-     *                 {@code false} otherwise.
-     * @return The result of the operation.
+     * @param subId The subId of the subscription to check whether satellite is enabled for.
+     * @param result The result receiver that returns whether the satellite modem is enabled
+     *               if the request is successful or an error code if the request failed.
      *
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
-    @SatelliteManager.SatelliteServiceResult public int isSatellitePowerOn(int subId,
-            @NonNull IBooleanConsumer callback) {
-        enforceSatelliteCommunicationPermission("isSatellitePowerOn");
+    public void requestIsSatelliteEnabled(int subId, @NonNull ResultReceiver result) {
+        enforceSatelliteCommunicationPermission("requestIsSatelliteEnabled");
+        if (!isSatelliteSupported()) {
+            result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+            return;
+        }
 
         final int validSubId = getValidSatelliteSubId(subId);
-        if (!isSatelliteEnabled(validSubId)) {
-            return SatelliteManager.SATELLITE_SERVICE_DISABLED;
+        if (!isSatelliteProvisioned(validSubId)) {
+            result.send(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED, null);
+            return;
         }
 
-        Phone phone = getPhoneOrDefault(validSubId, "isSatellitePowerOn");
+        Phone phone = getPhoneOrDefault(validSubId, "requestIsSatelliteEnabled");
         if (phone == null) {
-            return SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
+            result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+            return;
         }
 
-        Consumer<Boolean> argument = FunctionalUtils.ignoreRemoteException(callback::accept);
-        int result = (int) sendRequest(CMD_IS_SATELLITE_POWER_ON, argument, subId);
-        if (DBG) log("isSatellitePowerOn result: " + result);
-        return result;
+        sendRequest(CMD_IS_SATELLITE_ENABLED, result, subId);
     }
 
     /**
-     * Check whether the satellite service is supported on the device.
+     * Request to get whether the satellite service is supported on the device.
      *
-     * @param subId The subId to check satellite service support for.
-     * @param callback The callback that will be used to send the result if the operation is
-     *                 successful. Returns {@code true} if the satellite service is supported on
-     *                 the device and {@code false} otherwise.
-     * @return The result of the operation.
-     *
-     * @throws SecurityException if the caller doesn't have the required permission.
+     * @param subId The subId of the subscription to check satellite service support for.
+     * @param result The result receiver that returns whether the satellite service is supported on
+     *               the device if the request is successful or an error code if the request failed.
      */
     @Override
-    @SatelliteManager.SatelliteServiceResult public int isSatelliteSupported(int subId,
-            @NonNull IBooleanConsumer callback) {
-        enforceSatelliteCommunicationPermission("isSatelliteSupported");
-
-        final int validSubId = getValidSatelliteSubId(subId);
-        Phone phone = getPhoneOrDefault(validSubId, "isSatelliteSupported");
-        if (phone == null) {
-            return SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
+    public void requestIsSatelliteSupported(int subId, @NonNull ResultReceiver result) {
+        synchronized (mIsSatelliteSupportedLock) {
+            if (mIsSatelliteSupported != null) {
+                /* We have already successfully queried the satellite modem. */
+                Bundle bundle = new Bundle();
+                bundle.putBoolean(SatelliteManager.KEY_SATELLITE_SUPPORTED, mIsSatelliteSupported);
+                result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, bundle);
+                return;
+            }
         }
 
-        Consumer<Boolean> argument = FunctionalUtils.ignoreRemoteException(callback::accept);
-        int result = (int) sendRequest(CMD_IS_SATELLITE_SUPPORTED, argument, subId);
-        if (DBG) log("isSatelliteSupported result: " + result);
-        return result;
+        final int validSubId = getValidSatelliteSubId(subId);
+        Phone phone = getPhoneOrDefault(validSubId, "requestIsSatelliteSupported");
+        if (phone == null) {
+            result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+            return;
+        }
+
+        sendRequestAsync(CMD_IS_SATELLITE_SUPPORTED, result, phone, null);
     }
 
     /**
-     * Get the {@link SatelliteCapabilities} with all capabilities of the satellite service.
+     * Request to get the {@link SatelliteCapabilities} of the satellite service.
      *
-     * @param subId The subId to get the satellite capabilities for.
-     * @param callback The callback that will be used to send the {@link SatelliteCapabilities}
-     *                 if the operation is successful.
-     * @return The result of the operation.
+     * @param subId The subId of the subscription to get the satellite capabilities for.
+     * @param result The result receiver that returns the {@link SatelliteCapabilities}
+     *               if the request is successful or an error code if the request failed.
      *
      * @throws SecurityException if the caller doesn't have required permission.
      */
     @Override
-    @SatelliteManager.SatelliteServiceResult public int getSatelliteCapabilities(int subId,
-            @NonNull ISatelliteCapabilitiesConsumer callback) {
-        enforceSatelliteCommunicationPermission("getSatelliteCapabilities");
-
-        final int validSubId = getValidSatelliteSubId(subId);
-        Phone phone = getPhoneOrDefault(validSubId, "getSatelliteCapabilities");
-        if (phone == null) {
-            return SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
+    public void requestSatelliteCapabilities(int subId, @NonNull ResultReceiver result) {
+        enforceSatelliteCommunicationPermission("requestSatelliteCapabilities");
+        if (!isSatelliteSupported()) {
+            result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+            return;
         }
 
-        Consumer<SatelliteCapabilities> argument =
-                FunctionalUtils.ignoreRemoteException(callback::accept);
-        int result = (int) sendRequest(CMD_GET_SATELLITE_CAPABILITIES, argument, subId);
-        if (DBG) log("getSatelliteCapabilities result: " + result);
-        return result;
+        final int validSubId = getValidSatelliteSubId(subId);
+        Phone phone = getPhoneOrDefault(validSubId, "requestSatelliteCapabilities");
+        if (phone == null) {
+            result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+            return;
+        }
+
+        sendRequestAsync(CMD_GET_SATELLITE_CAPABILITIES, result, phone, null);
     }
 
     /**
@@ -12740,31 +12889,41 @@
      * This can be called by the pointing UI when the user starts pointing to the satellite.
      * Modem should continue to report the pointing input as the device or satellite moves.
      *
-     * @param subId The subId to start satellite position updates for.
+     * @param subId The subId of the subscription to start satellite position updates for.
+     * @param errorCallback The callback to get the error code of the request.
      * @param callback The callback to notify of changes in satellite position.
-     * @return The result of the operation.
      *
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
-    @SatelliteManager.SatelliteServiceResult public int startSatellitePositionUpdates(int subId,
+    public void startSatellitePositionUpdates(int subId, @NonNull IIntegerConsumer errorCallback,
             @NonNull ISatelliteStateListener callback) {
         enforceSatelliteCommunicationPermission("startSatellitePositionUpdates");
+        Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(errorCallback::accept);
+        if (!isSatelliteSupported()) {
+            result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+            return;
+        }
 
         final int validSubId = getValidSatelliteSubId(subId);
-        if (!isSatelliteEnabled(validSubId)) {
-            return SatelliteManager.SATELLITE_SERVICE_DISABLED;
+        if (!isSatelliteProvisioned(validSubId)) {
+            result.accept(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED);
+            return;
         }
 
         Phone phone = getPhoneOrDefault(validSubId, "startSatellitePositionUpdates");
         if (phone == null) {
-            return SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
+            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            return;
         }
 
         if (mSatellitePositionUpdateHandlers.containsKey(callback.asBinder())) {
-            log("startSatellitePositionUpdates: callback already registered: "
-                    + callback.asBinder());
-            return SatelliteManager.SATELLITE_SERVICE_CALLBACK_ALREADY_REGISTERED;
+            if (DBG) {
+                log("startSatellitePositionUpdates: callback already registered: "
+                        + callback.asBinder());
+            }
+            result.accept(SatelliteManager.SATELLITE_ERROR_NONE);
+            return;
         }
 
         SatellitePositionUpdateHandler handler =
@@ -12775,42 +12934,48 @@
                 SatellitePositionUpdateHandler.EVENT_MESSAGE_TRANSFER_STATE_UPDATE, null);
         mSatellitePositionUpdateHandlers.put(callback.asBinder(), handler);
 
-        int result = (int) sendRequest(CMD_START_SATELLITE_POSITION_UPDATES, null, validSubId);
-        if (DBG) log("startSatellitePositionUpdates result: " + result);
-        return result;
+        sendRequestAsync(CMD_START_SATELLITE_POSITION_UPDATES, result, phone, null);
     }
 
     /**
      * Stop receiving satellite position updates.
      * This can be called by the pointing UI when the user stops pointing to the satellite.
      *
-     * @param subId The subId to stop satellite position updates for.
-     * @param callback The callback that was passed in {@link
-     *                   #startSatellitePositionUpdates(int, ISatelliteStateListener)}
-     * @return The result of the operation.
+     * @param subId The subId of the subscription to stop satellite position updates for.
+     * @param errorCallback The callback to get the error code of the request.
+     * @param callback The callback that was passed to {@link
+     * #startSatellitePositionUpdates(int, IIntegerConsumer, ISatelliteStateListener)}
      *
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
-    @SatelliteManager.SatelliteServiceResult public int stopSatellitePositionUpdates(int subId,
+    public void stopSatellitePositionUpdates(int subId, @NonNull IIntegerConsumer errorCallback,
             @NonNull ISatelliteStateListener callback) {
         enforceSatelliteCommunicationPermission("stopSatellitePositionUpdates");
+        Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(errorCallback::accept);
+        if (!isSatelliteSupported()) {
+            result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+            return;
+        }
 
         final int validSubId = getValidSatelliteSubId(subId);
-        if (!isSatelliteEnabled(validSubId)) {
-            return SatelliteManager.SATELLITE_SERVICE_DISABLED;
+        if (!isSatelliteProvisioned(validSubId)) {
+            result.accept(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED);
+            return;
         }
 
         Phone phone = getPhoneOrDefault(validSubId, "stopSatellitePositionUpdates");
         if (phone == null) {
-            return SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
+            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            return;
         }
 
         SatellitePositionUpdateHandler handler =
                 mSatellitePositionUpdateHandlers.remove(callback.asBinder());
         if (handler == null) {
-            loge("stopSatellitePositionUpdates: No SatellitePositionArgument");
-            return SatelliteManager.SATELLITE_SERVICE_CALLBACK_NOT_REGISTERED;
+            loge("stopSatellitePositionUpdates: No SatellitePositionUpdateHandler");
+            result.accept(SatelliteManager.SATELLITE_INVALID_ARGUMENTS);
+            return;
         } else {
             phone.unregisterForSatellitePointingInfoChanged(handler);
             phone.unregisterForSatelliteMessagesTransferComplete(handler);
@@ -12818,42 +12983,45 @@
 
         if (!mSatellitePositionUpdateHandlers.isEmpty()) {
             log("stopSatellitePositionUpdates: other listeners still exist.");
-            return SatelliteManager.SATELLITE_SERVICE_SUCCESS;
+            result.accept(SatelliteManager.SATELLITE_ERROR_NONE);
+            return;
         }
 
-        int result = (int) sendRequest(CMD_STOP_SATELLITE_POSITION_UPDATES, null, validSubId);
-        if (DBG) log("stopSatellitePositionUpdates result: " + result);
-        return result;
+        sendRequestAsync(CMD_STOP_SATELLITE_POSITION_UPDATES, result, phone, null);
     }
 
     /**
-     * Get maximum number of characters per text message on satellite.
-     * @param subId - The subId of the subscription.
-     * @param callback - The callback that will be used to send maximum characters limit
-     *                 if operation is successful.
-     * @return The result of the operation.
+     * Request to get the maximum number of characters per text message on satellite.
+     *
+     * @param subId The subId of the subscription to get the maximum number of characters for.
+     * @param result The result receiver that returns the maximum number of characters per text
+     *               message on satellite if the request is successful or an error code
+     *               if the request failed.
      *
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
-    public int getMaxCharactersPerSatelliteTextMessage(int subId, IIntegerConsumer callback) {
-        enforceSatelliteCommunicationPermission("getMaxCharactersPerSatelliteTextMessage");
+    public void requestMaxCharactersPerSatelliteTextMessage(int subId,
+            @NonNull ResultReceiver result) {
+        enforceSatelliteCommunicationPermission("requestMaxCharactersPerSatelliteTextMessage");
+        if (!isSatelliteSupported()) {
+            result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+            return;
+        }
 
         final int validSubId = getValidSatelliteSubId(subId);
-        if (!isSatelliteEnabled(validSubId)) {
-            return SatelliteManager.SATELLITE_SERVICE_DISABLED;
+        if (!isSatelliteProvisioned(validSubId)) {
+            result.send(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED, null);
+            return;
         }
 
-        Phone phone = getPhoneOrDefault(validSubId, "getMaxCharactersPerSatelliteTextMessage");
+        Phone phone = getPhoneOrDefault(validSubId, "requestMaxCharactersPerSatelliteTextMessage");
         if (phone == null) {
-            return SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
+            result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+            return;
         }
 
-        Consumer<Integer> argument = FunctionalUtils.ignoreRemoteException(callback::accept);
-        int result = (int) sendRequest(
-                CMD_GET_MAX_CHAR_PER_SATELLITE_TEXT_MSG, argument, validSubId);
-        if (DBG) log("getMaxCharPerTextMessageOnSatellite result: " + result);
-        return result;
+        sendRequestAsync(CMD_GET_MAX_CHAR_PER_SATELLITE_TEXT_MSG, result, phone, null);
     }
 
     /**
@@ -12861,191 +13029,451 @@
      * This is needed to register the subscription if the provider allows dynamic registration.
      *
      * @param subId The subId of the subscription to be provisioned.
-     * @param features List of features 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.
+     *
+     * @return The signal transport used by the caller to cancel the provision request,
+     *         or {@code null} if the request failed.
+     *
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
-    public ICancellationSignal provisionSatelliteService(int subId, int[] features,
-            IIntegerConsumer callback) {
+    @Nullable public ICancellationSignal provisionSatelliteService(int subId,
+            @NonNull String token, @NonNull IIntegerConsumer callback) {
         enforceSatelliteCommunicationPermission("provisionSatelliteService");
+        Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
+        if (!isSatelliteSupported()) {
+            result.accept(SatelliteManager.SATELLITE_NOT_SUPPORTED);
+            return null;
+        }
 
         final int validSubId = getValidSatelliteSubId(subId);
-        String callbackRemoteExErrorMessage = "provisionSatelliteService: callback not available.";
         Phone phone = getPhoneOrDefault(validSubId, "provisionSatelliteService");
         if (phone == null) {
-            sendResponse(callback, SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE,
-                    callbackRemoteExErrorMessage);
+            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
             return null;
         }
 
         if (mSatelliteProvisionCallbacks.containsKey(validSubId)) {
-            sendResponse(callback, SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS,
-                    callbackRemoteExErrorMessage);
+            result.accept(SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS);
             return null;
         }
 
-        if (isSatelliteEnabled(validSubId)) {
-            sendResponse(callback, SatelliteManager.SATELLITE_SERVICE_ALREADY_PROVISIONED,
-                    callbackRemoteExErrorMessage);
+        if (isSatelliteProvisioned(validSubId)) {
+            result.accept(SatelliteManager.SATELLITE_ERROR_NONE);
             return null;
         }
 
         sendRequestAsync(CMD_PROVISION_SATELLITE_SERVICE,
-                new ProvisionSatelliteServiceArgument(features, callback, validSubId), phone, null);
+                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.
+     * @param subId The subId of the subscription to register for provision state changes.
      * @param callback The callback to handle the satellite provision state changed event.
+     *
+     * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+     *
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
-    @SatelliteManager.SatelliteServiceResult
-    public int registerForSatelliteProvisionStateChanged(
-            int subId, ISatelliteStateListener callback) {
-        //enforceSatelliteCommunicationPermission("registerForSatelliteProvisionStateChanged");
-
-        final int validSubId = getValidSatelliteSubId(subId);
-        Phone phone = getPhoneOrDefault(
-                validSubId, "registerForSatelliteProvisionStateChanged");
-        if (phone == null) {
-            return SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
-        }
-
-        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);
-        return SatelliteManager.SATELLITE_SERVICE_SUCCESS;
+    @SatelliteManager.SatelliteError public int registerForSatelliteProvisionStateChanged(int subId,
+            @NonNull ISatelliteStateListener callback) {
+        enforceSatelliteCommunicationPermission("registerForSatelliteProvisionStateChanged");
+        return registerForSatelliteProvisionStateChangedInternal(subId, callback);
     }
 
     /**
      * Unregister for the satellite provision state change.
      *
-     * @param subId The subId of the subscription associated with the satellite service.
-     * @param callback The callback that was passed to
-     * {@link #registerForSatelliteProvisionStateChanged(int, ISatelliteStateListener)}
+     * @param subId The subId of the subscription to unregister for provision state changes.
+     * @param errorCallback The callback to get the error code of the request.
+     * @param callback The callback that was passed to {@link
+     *                 #registerForSatelliteProvisionStateChanged(int, ISatelliteStateListener)}.
+     *
+     * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+     *
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
-    @SatelliteManager.SatelliteServiceResult
-    public int unregisterForSatelliteProvisionStateChanged(
-            int subId, ISatelliteStateListener callback) {
+    @SatelliteManager.SatelliteError public int unregisterForSatelliteProvisionStateChanged(
+            int subId, @NonNull ISatelliteStateListener callback) {
         enforceSatelliteCommunicationPermission("unregisterForSatelliteProvisionStateChanged");
+        if (!isSatelliteSupported()) {
+            return SatelliteManager.SATELLITE_NOT_SUPPORTED;
+        }
 
         final int validSubId = getValidSatelliteSubId(subId);
         SatelliteProvisionStateChangedHandler satelliteProvisionStateChangedHandler =
                 mSatelliteProvisionStateChangedHandlers.get(validSubId);
         if (satelliteProvisionStateChangedHandler != null) {
             if (satelliteProvisionStateChangedHandler.removeListener(callback)) {
-                return SatelliteManager.SATELLITE_SERVICE_SUCCESS;
+                return SatelliteManager.SATELLITE_ERROR_NONE;
             }
         }
-        return SatelliteManager.SATELLITE_SERVICE_CALLBACK_NOT_REGISTERED;
+        return SatelliteManager.SATELLITE_INVALID_ARGUMENTS;
     }
 
     /**
-     * Get the list of provisioned satellite features.
+     * Request to get whether the device is provisioned with a satellite provider.
      *
-     * @param subId The subId of the subscription associated with the satellite service.
-     * @param callback The callback to get the list of provisioned satellite features.
-     * @return The error code of the request.
+     * @param subId The subId of the subscription to get whether the device is provisioned for.
+     * @param result The result receiver that returns whether the device is provisioned with a
+     *               satellite provider if the request is successful or an error code if the
+     *               request failed.
+     *
      * @throws SecurityException if the caller doesn't have the required permission.
      */
     @Override
-    @SatelliteManager.SatelliteServiceResult
-    public int getProvisionedSatelliteFeatures(int subId, IIntArrayConsumer callback) {
-        enforceSatelliteCommunicationPermission("getProvisionedSatelliteFeatures");
+    public void requestIsSatelliteProvisioned(int subId, @NonNull ResultReceiver result) {
+        enforceSatelliteCommunicationPermission("requestIsSatelliteProvisioned");
+        if (!isSatelliteSupported()) {
+            result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+            return;
+        }
 
         final int validSubId = getValidSatelliteSubId(subId);
-        if (!isSatelliteEnabled(validSubId)) {
-            return SatelliteManager.SATELLITE_SERVICE_DISABLED;
-        }
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED,
+                isSatelliteProvisioned(validSubId));
+        result.send(SatelliteManager.SATELLITE_ERROR_NONE, bundle);
+    }
 
-        final Phone phone = getPhoneOrDefault(validSubId, "getProvisionedSatelliteFeatures");
+    /**
+     * Register for listening to satellite state changes.
+     *
+     * @param subId The subId of the subscription to register for satellite modem state changes.
+     * @param callback The callback to handle the satellite state change event.
+     *
+     * @return The {@link SatelliteManager.SatelliteError} 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) {
-            return SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
+            return SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
         }
 
-        int result = (int) sendRequest(
-                CMD_GET_PROVISIONED_SATELLITE_FEATURES, callback, validSubId);
-        if (DBG) log("getProvisionedSatelliteFeatures result: " + result);
+        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 of the subscription to unregister for satellite modem state changes.
+     * @param callback The callback that was passed to
+     *                 {@link #registerForSatelliteModemStateChange(int, ISatelliteStateListener)}.
+     *
+     * @return The {@link SatelliteManager.SatelliteError} 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 of the subscription to register for incoming satellite datagrams.
+     * @param datagramType Type of datagram.
+     * @param callback The callback to handle incoming datagrams over satellite.
+     *
+     * @return The {@link SatelliteManager.SatelliteError} 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 of the subscription to unregister for incoming satellite datagrams.
+     * @param callback The callback that was passed to
+     *                 {@link #registerForSatelliteDatagram(int, int, ISatelliteStateListener)}.
+     *
+     * @return The {@link SatelliteManager.SatelliteError} 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 to poll pending satellite datagrams for.
+     *
+     * @return The {@link SatelliteManager.SatelliteError} 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;
     }
 
+    /**
+     * Send datagram over satellite.
+     *
+     * @param subId The subId of the subscription to send satellite datagrams for.
+     * @param datagramType Type of datagram.
+     * @param datagram Datagram to send over satellite.
+     * @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 sendSatelliteDatagram(int subId, @SatelliteManager.DatagramType int datagramType,
+            SatelliteDatagram datagram, IIntegerConsumer callback) {
+        enforceSatelliteCommunicationPermission("sendSatelliteDatagram");
+        Consumer<Integer> result = FunctionalUtils.ignoreRemoteException(callback::accept);
+
+        final int validSubId = getValidSatelliteSubId(subId);
+        if (!isSatelliteProvisioned(validSubId)) {
+            result.accept(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED);
+            return;
+        }
+
+        Phone phone = getPhoneOrDefault(validSubId, "sendSatelliteDatagram");
+        if (phone == null) {
+            result.accept(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE);
+            return;
+        }
+
+        // check if we need to start PointingUI.
+
+        sendRequestAsync(CMD_SEND_SATELLITE_DATAGRAM,
+                new SendSatelliteDatagramArgument(datagramType, datagram, result),
+                phone, null);
+    }
+
+    /**
+     * Request to get whether satellite communication is allowed for the current location.
+     *
+     * @param subId The subId of the subscription to check whether satellite communication is
+     *              allowed for the current location for.
+     * @param result The result receiver that returns whether satellite communication is allowed
+     *               for the current location if the request is successful or an error code
+     *               if the request failed.
+     *
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    public void requestIsSatelliteCommunicationAllowedForCurrentLocation(int subId,
+            @NonNull ResultReceiver result) {
+        enforceSatelliteCommunicationPermission(
+                "requestIsSatelliteCommunicationAllowedForCurrentLocation");
+        if (!isSatelliteSupported()) {
+            result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+            return;
+        }
+
+        final int validSubId = getValidSatelliteSubId(subId);
+        if (!isSatelliteProvisioned(validSubId)) {
+            result.send(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED, null);
+            return;
+        }
+
+        Phone phone = getPhoneOrDefault(validSubId,
+                "requestIsSatelliteCommunicationAllowedForCurrentLocation");
+        if (phone == null) {
+            result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+            return;
+        }
+
+        sendRequest(CMD_IS_SATELLITE_COMMUNICATION_ALLOWED, result, subId);
+    }
+
+    /**
+     * Request to get the time after which the satellite will next be visible
+     *
+     * @param subId The subId to get the time after which the satellite will next be visible for.
+     * @param result The result receiver that returns the time after which the satellite will next
+     *               be visible if the request is successful or an error code if the request failed.
+     *
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    public void requestTimeForNextSatelliteVisibility(int subId, @NonNull ResultReceiver result) {
+        enforceSatelliteCommunicationPermission("requestTimeForNextSatelliteVisibility");
+        if (!isSatelliteSupported()) {
+            result.send(SatelliteManager.SATELLITE_NOT_SUPPORTED, null);
+            return;
+        }
+
+        final int validSubId = getValidSatelliteSubId(subId);
+        if (!isSatelliteProvisioned(validSubId)) {
+            result.send(SatelliteManager.SATELLITE_SERVICE_NOT_PROVISIONED, null);
+            return;
+        }
+
+        Phone phone = getPhoneOrDefault(validSubId, "requestTimeForNextSatelliteVisibility");
+        if (phone == null) {
+            result.send(SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE, null);
+            return;
+        }
+
+        sendRequestAsync(CMD_GET_TIME_SATELLITE_NEXT_VISIBLE, result, phone, null);
+    }
+
     private void handleCmdProvisionSatelliteService(@NonNull ProvisionSatelliteServiceArgument arg,
             @NonNull Phone phone, Message onCompleted) {
-        String callbackRemoteExErrorMessage =
-                "handleCmdProvisionSatelliteService: callback not available.";
-        if (arg == null) {
-            loge("handleCmdProvisionSatelliteService: arg is null");
-            return;
-        }
-        if (phone == null) {
-            loge("handleCmdProvisionSatelliteService: phone is null");
-            sendResponse(arg.callback, SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE,
-                    callbackRemoteExErrorMessage);
-            return;
-        }
-
         if (!mSatelliteProvisionCallbacks.containsKey(arg.subId)) {
             mSatelliteProvisionCallbacks.put(arg.subId, arg.callback);
-            phone.provisionSatelliteService(onCompleted, phone.getImei(), phone.getMsisdn(),
-                    getSatelliteImsi(arg.subId), arg.features);
+            phone.provisionSatelliteService(onCompleted, arg.token);
         } else {
-            sendResponse(arg.callback, SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS,
-                    callbackRemoteExErrorMessage);
+            arg.callback.accept(SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS);
         }
     }
 
-    private void handleEventProvisionSatelliteServiceDone(MainThreadRequest request) {
-        final ProvisionSatelliteServiceArgument arg =
-                (ProvisionSatelliteServiceArgument) request.argument;
-        if (arg == null) {
-            loge("handleEventProvisionSatelliteServiceDone: arg is null");
-            return;
-        }
+    private void handleEventProvisionSatelliteServiceDone(
+            @NonNull ProvisionSatelliteServiceArgument arg,
+            @SatelliteManager.SatelliteError int result) {
+        log("handleEventProvisionSatelliteServiceDone: result="
+                + result + ", subId=" + arg.subId);
 
-        IIntegerConsumer callback = mSatelliteProvisionCallbacks.remove(arg.subId);
+        Consumer<Integer> callback = mSatelliteProvisionCallbacks.remove(arg.subId);
         if (callback == null) {
             loge("handleEventProvisionSatelliteServiceDone: callback is null for subId="
                     + arg.subId);
             return;
         }
+        callback.accept(result);
 
-        int result = (int) request.result;
-        if (DBG) {
-            log("handleEventProvisionSatelliteServiceDone: result="
-                    + result + ", subId=" + arg.subId);
-        }
-        sendResponse(callback, result,
-                "handleEventProvisionSatelliteServiceDone: callback not available.");
-
-        if (result == SatelliteManager.SATELLITE_SERVICE_SUCCESS) {
-            setSatelliteEnabled(arg.subId, true);
+        if (result == SatelliteManager.SATELLITE_ERROR_NONE) {
+            setSatelliteProvisioned(arg.subId, true);
         }
 
         /**
@@ -13053,38 +13481,43 @@
          * or SatelliteController.
          * TODO (b/267826133) we need to do this for all subscriptions on the device.
          */
-        registerForSatelliteProvisionStateChanged(arg.subId, null);
+        registerForSatelliteProvisionStateChangedInternal(arg.subId, 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,
+            @NonNull 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 void sendResponse(@NonNull IIntegerConsumer callback, int result, String message) {
-        Objects.requireNonNull(callback);
-
-        try {
-            callback.accept(result);
-        } catch (RemoteException e) {
-            Log.w(LOG_TAG, message);
+    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);
 
-    private String getSatelliteImsi(int subId) {
-        if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
-            return "";
+        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) {
@@ -13101,15 +13534,15 @@
     }
 
     /**
-     * Check if satellite is enabled for a subscription.
+     * Check if satellite is provisioned for a subscription or the device.
      *
      * Note: this is the version without permission check for telephony internal use only. The
      * caller need to take care of the permission check.
      */
-    private boolean isSatelliteEnabled(int subId) {
+    private boolean isSatelliteProvisioned(int subId) {
         if (subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
             if (mSubscriptionController == null) {
-                loge("setSatelliteEnabled mSubscriptionController is null");
+                loge("isSatelliteProvisioned mSubscriptionController is null");
                 return false;
             }
 
@@ -13126,22 +13559,22 @@
     }
 
     /**
-     * Set satellite enabled for a subscription.
+     * Set satellite provisioned for a subscription or the device.
      *
      * The permission {@link android.Manifest.permission#MODIFY_PHONE_STATE} will be enforced by
      * {@link SubscriptionController} when setting satellite enabled for an active subscription.
      * Otherwise, {@link android.Manifest.permission#SATELLITE_COMMUNICATION} will be enforced.
      */
-    private void setSatelliteEnabled(int subId, boolean isEnabled) {
+    private synchronized void setSatelliteProvisioned(int subId, boolean isEnabled) {
         if (subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
             if (mSubscriptionController == null) {
-                loge("setSatelliteEnabled mSubscriptionController is null");
+                loge("setSatelliteProvisioned mSubscriptionController is null");
                 return;
             }
             mSubscriptionController.setSubscriptionProperty(
                     subId, SubscriptionManager.SATELLITE_ENABLED, isEnabled ? "1" : "0");
         } else {
-            //TODO (b/267826133): enable via SatelliteController
+            //TODO (b/267826133): set via SatelliteController
         }
     }
 
@@ -13166,6 +13599,96 @@
     }
 
     /**
+     * If we have not successfully queried the satellite modem for its satellite service support,
+     * we will retry the query one more time. Otherwise, we will return the queried result.
+     */
+    private boolean isSatelliteSupported() {
+        synchronized (mIsSatelliteSupportedLock) {
+            if (mIsSatelliteSupported != null) {
+                /* We have already successfully queried the satellite modem. */
+                return mIsSatelliteSupported;
+            }
+        }
+        /**
+         * We have not successfully checked whether the modem supports satellite service.
+         * Thus, we need to retry it now.
+         */
+        requestIsSatelliteSupported(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
+                mSatelliteSupportedReceiver);
+        return false;
+    }
+
+    /**
+     * Get the {@link SatelliteManager.SatelliteError} from the provided result.
+     *
+     * @param ar AsyncResult used to determine the error code.
+     * @param caller The satellite request.
+     * @param checkResult Whether to check if the result exists.
+     *
+     * @return The {@link SatelliteManager.SatelliteError} error code from the request.
+     */
+    @SatelliteManager.SatelliteError private int getSatelliteError(@NonNull AsyncResult ar,
+            @NonNull String caller, boolean checkResult) {
+        int errorCode;
+        if (ar.exception == null) {
+            errorCode = SatelliteManager.SATELLITE_ERROR_NONE;
+            if (checkResult && ar.result == null) {
+                loge(caller + ": result is null");
+                errorCode = SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+            }
+        } else {
+            errorCode = SatelliteManager.SATELLITE_ERROR;
+            if (ar.exception instanceof CommandException) {
+                CommandException.Error error =
+                        ((CommandException) (ar.exception)).getCommandError();
+                errorCode = RILUtils.convertToSatelliteError(error);
+                loge(caller + " CommandException: " + ar.exception);
+            } else {
+                loge(caller + " unknown exception: " + ar.exception);
+            }
+        }
+        log(caller + " error: " + errorCode);
+        return errorCode;
+    }
+
+    /**
+     * Register for the satellite provision state change.
+     *
+     * @param subId The subId of the subscription associated with the satellite service.
+     * @param callback The callback to handle the satellite provision state changed event.
+     *
+     * @return The {@link SatelliteManager.SatelliteError} result of the operation.
+     */
+    @SatelliteManager.SatelliteError private int registerForSatelliteProvisionStateChangedInternal(
+            int subId, @Nullable ISatelliteStateListener callback) {
+        if (!isSatelliteSupported()) {
+            return SatelliteManager.SATELLITE_NOT_SUPPORTED;
+        }
+
+        final int validSubId = getValidSatelliteSubId(subId);
+        Phone phone = getPhoneOrDefault(validSubId, "registerForSatelliteProvisionStateChanged");
+        if (phone == null) {
+            return SatelliteManager.SATELLITE_INVALID_TELEPHONY_STATE;
+        }
+
+        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);
+        return 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:
@@ -13190,6 +13713,13 @@
     }
 
     /**
+     * @return The subscription manager service instance.
+     */
+    public SubscriptionManagerService getSubscriptionManagerService() {
+        return SubscriptionManagerService.getInstance();
+    }
+
+    /**
      * Class binds the consumer[callback] and carrierId.
      */
     private static class CallerCallbackInfo {
@@ -13209,4 +13739,14 @@
             return mCarrierId;
         }
     }
-}
\ No newline at end of file
+
+    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;
+    }
+}
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 519655e..2c43c7f 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -593,38 +593,36 @@
                     new DomainSelectionConnection.DomainSelectionConnectionCallback() {
         @Override
         public void onSelectionTerminated(@DisconnectCauses int cause) {
-            if (mEmergencyCallDomainSelectionConnection != null) {
+            mDomainSelectionMainExecutor.execute(() -> {
                 Log.i(this, "onSelectionTerminated cause=" + cause);
+                if (mEmergencyCallDomainSelectionConnection == null) {
+                    Log.i(this, "onSelectionTerminated no DomainSelectionConnection");
+                    return;
+                }
 
                 // Cross stack redial
                 if (cause == android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE
                         || cause == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE) {
                     if (mEmergencyConnection != null) {
-                        final boolean isPermanentFailure =
+                        boolean isPermanentFailure =
                                 cause == android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE;
-                        Log.i(this, "onSelectionTerminated trigger cross stack redial"
-                                + " permanent=" + isPermanentFailure);
-                        mDomainSelectionMainExecutor.execute(() -> {
-                            Log.i(this, "onSelectionTerminated execute cross stack redial"
-                                    + " permanent=" + isPermanentFailure);
-                            TelephonyConnection c = mEmergencyConnection;
-                            Phone phone = mEmergencyCallDomainSelectionConnection.getPhone();
-                            mEmergencyConnection.removeTelephonyConnectionListener(
-                                    mEmergencyConnectionListener);
-                            mEmergencyStateTracker.endCall(
-                                    mEmergencyConnection.getTelecomCallId());
-                            releaseEmergencyCallDomainSelection(true);
-                            retryOutgoingOriginalConnection(c, phone, isPermanentFailure);
-                        });
+                        Log.i(this, "onSelectionTerminated permanent=" + isPermanentFailure);
+                        TelephonyConnection c = mEmergencyConnection;
+                        Phone phone = mEmergencyCallDomainSelectionConnection.getPhone();
+                        mEmergencyConnection.removeTelephonyConnectionListener(
+                                mEmergencyConnectionListener);
+                        releaseEmergencyCallDomainSelection(true);
+                        mEmergencyStateTracker.endCall(mEmergencyCallId);
+                        mEmergencyCallId = null;
+                        retryOutgoingOriginalConnection(c, phone, isPermanentFailure);
                         return;
                     }
                 }
-                mEmergencyCallDomainSelectionConnection = null;
                 if (mEmergencyConnection != null) {
                     mEmergencyConnection.hangup(android.telephony.DisconnectCause.OUT_OF_NETWORK);
                     mEmergencyConnection = null;
                 }
-            }
+            });
         }
     };
 
@@ -2386,9 +2384,9 @@
 
         Log.i(this, "maybeReselectDomainForEmergencyCall endCall()");
         c.removeTelephonyConnectionListener(mEmergencyConnectionListener);
-        mEmergencyStateTracker.endCall(c.getTelecomCallId());
         releaseEmergencyCallDomainSelection(true);
-
+        mEmergencyStateTracker.endCall(c.getTelecomCallId());
+        mEmergencyCallId = null;
         return false;
     }
 
@@ -2410,7 +2408,7 @@
                 + ", psCause:" + reasonInfo);
 
         if (mDomainSelectionConnection != null && c.getOriginalConnection() != null) {
-            Phone phone = c.getPhone();
+            Phone phone = c.getPhone().getDefaultPhone();
             final String number = c.getAddress().getSchemeSpecificPart();
             int videoState = c.getOriginalConnection().getVideoState();
             SelectionAttributes selectionAttributes = NormalCallDomainSelectionConnection
@@ -2424,7 +2422,7 @@
                     .reselectDomain(selectionAttributes);
             if (future != null) {
                 future.thenAcceptAsync((result) -> {
-                    onNormalCallRedial(c, result, videoState);
+                    onNormalCallRedial(c, phone, result, videoState);
                 }, mDomainSelectionMainExecutor);
                 return true;
             }
@@ -2598,14 +2596,13 @@
         onEmergencyRedialOnDomain(connection, phone, result);
     }
 
-    private void onNormalCallRedial(TelephonyConnection connection,
+    private void onNormalCallRedial(TelephonyConnection connection, Phone phone,
             @NetworkRegistrationInfo.Domain int domain, int videocallState) {
 
         Log.v(LOG_TAG, "Redialing the call in domain:"
                 + DomainSelectionService.getDomainName(domain));
 
         String number = connection.getAddress().getSchemeSpecificPart();
-        Phone phone = connection.getPhone();
 
         Bundle extras = new Bundle();
         extras.putInt(PhoneConstants.EXTRA_DIAL_DOMAIN, domain);
@@ -2647,8 +2644,8 @@
         if (TextUtils.equals(mEmergencyCallId, c.getTelecomCallId())) {
             Log.i(this, "onLocalHangup " + mEmergencyCallId);
             c.removeTelephonyConnectionListener(mEmergencyConnectionListener);
-            mEmergencyStateTracker.endCall(c.getTelecomCallId());
             releaseEmergencyCallDomainSelection(true);
+            mEmergencyStateTracker.endCall(c.getTelecomCallId());
             mEmergencyCallId = null;
         }
     }
diff --git a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
index e8825be..a23d0b7 100644
--- a/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelector.java
@@ -255,7 +255,7 @@
 
         if (result.getAccessNetwork() == UNKNOWN) {
             if ((mPreferredNetworkScanType == SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE)
-                      || (mScanType == DomainSelectionService.SCAN_TYPE_FULL_SERVICE)) {
+                      && (mScanType == DomainSelectionService.SCAN_TYPE_FULL_SERVICE)) {
                 mScanType = DomainSelectionService.SCAN_TYPE_LIMITED_SERVICE;
                 mWwanSelectorCallback.onRequestEmergencyNetworkScan(
                         mLastPreferredNetworks, mScanType, mCancelSignal,
diff --git a/tests/src/com/android/phone/PhoneInterfaceManagerTest.java b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
index 4cc793d..e702279 100644
--- a/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
+++ b/tests/src/com/android/phone/PhoneInterfaceManagerTest.java
@@ -21,7 +21,6 @@
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -42,9 +41,10 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.TelephonyTestBase;
+import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.IIntegerConsumer;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -54,7 +54,7 @@
 import java.util.Locale;
 
 /**
- * Unit Test for CarrierConfigLoader.
+ * Unit Test for PhoneInterfaceManager.
  */
 @RunWith(AndroidJUnit4.class)
 public class PhoneInterfaceManagerTest extends TelephonyTestBase {
@@ -67,6 +67,9 @@
     @Mock
     Phone mPhone;
 
+    @Mock
+    private SubscriptionManagerService mSubscriptionManagerService;
+
     @Before
     @UiThreadTest
     public void setUp() throws Exception {
@@ -76,6 +79,9 @@
         // alive on a test devices. You must use the spy to mock behavior. Mocks stemming from the
         // passed context will remain unused.
         mPhoneInterfaceManager = spy(PhoneInterfaceManager.init(mPhoneGlobals));
+        doReturn(mSubscriptionManagerService).when(mPhoneInterfaceManager)
+                .getSubscriptionManagerService();
+        TelephonyManager.setupISubForTest(mSubscriptionManagerService);
         mSharedPreferences = mPhoneInterfaceManager.getSharedPreferences();
         mSharedPreferences.edit().remove(Phone.PREF_NULL_CIPHER_AND_INTEGRITY_ENABLED).commit();
         mIIntegerConsumer = mock(IIntegerConsumer.class);
diff --git a/tests/src/com/android/phone/euicc/EuiccUiDispatcherActivityTest.java b/tests/src/com/android/phone/euicc/EuiccUiDispatcherActivityTest.java
index 8b0ac5c..817220c 100644
--- a/tests/src/com/android/phone/euicc/EuiccUiDispatcherActivityTest.java
+++ b/tests/src/com/android/phone/euicc/EuiccUiDispatcherActivityTest.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.service.euicc.EuiccService;
 import android.telephony.euicc.EuiccManager;
 
 import androidx.test.InstrumentationRegistry;
@@ -111,6 +112,24 @@
         assertEquals("bar", euiccUiIntent.getStringExtra("foo"));
     }
 
+    @Test
+    public void testTransferEmbeddedSubscriptionsAction() {
+        mIntent = new Intent(EuiccManager.ACTION_TRANSFER_EMBEDDED_SUBSCRIPTIONS);
+        Intent euiccUiIntent = mActivity.resolveEuiccUiIntent();
+        assertNotNull(euiccUiIntent);
+        assertEquals(EuiccService.ACTION_TRANSFER_EMBEDDED_SUBSCRIPTIONS,
+                euiccUiIntent.getAction());
+    }
+
+    @Test
+    public void testConvertToEmbeddedSubscriptionAction() {
+        mIntent = new Intent(EuiccManager.ACTION_CONVERT_TO_EMBEDDED_SUBSCRIPTION);
+        Intent euiccUiIntent = mActivity.resolveEuiccUiIntent();
+        assertNotNull(euiccUiIntent);
+        assertEquals(EuiccService.ACTION_CONVERT_TO_EMBEDDED_SUBSCRIPTION,
+                euiccUiIntent.getAction());
+    }
+
     class TestEuiccUiDispatcherActivity extends EuiccUiDispatcherActivity {
         public TestEuiccUiDispatcherActivity() {
             attachBaseContext(mMockContext);
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 61060d9..4826d89 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -18,6 +18,7 @@
 
 import static android.telephony.DisconnectCause.EMERGENCY_PERM_FAILURE;
 import static android.telephony.DisconnectCause.EMERGENCY_TEMP_FAILURE;
+import static android.telephony.DisconnectCause.ERROR_UNSPECIFIED;
 import static android.telephony.DisconnectCause.NOT_DISCONNECTED;
 import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
 import static android.telephony.NetworkRegistrationInfo.DOMAIN_CS;
@@ -1952,6 +1953,40 @@
     }
 
     @Test
+    public void testOnSelectionTerminatedUnspecified() throws Exception {
+        setupForCallTest();
+
+        doReturn(mEmergencyCallDomainSelectionConnection).when(mDomainSelectionResolver)
+                .getDomainSelectionConnection(any(), anyInt(), eq(true));
+        doReturn(mPhone0).when(mEmergencyCallDomainSelectionConnection).getPhone();
+        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(anyString());
+
+        doReturn(true).when(mDomainSelectionResolver).isDomainSelectionSupported();
+        doReturn(mImsPhone).when(mPhone0).getImsPhone();
+
+        mTestConnectionService.onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1,
+                createConnectionRequest(PHONE_ACCOUNT_HANDLE_1,
+                        TEST_EMERGENCY_NUMBER, TELECOM_CALL_ID1));
+
+        ArgumentCaptor<DomainSelectionConnection.DomainSelectionConnectionCallback> callbackCaptor =
+                ArgumentCaptor.forClass(
+                        DomainSelectionConnection.DomainSelectionConnectionCallback.class);
+
+        verify(mEmergencyCallDomainSelectionConnection).createEmergencyConnection(
+                any(), callbackCaptor.capture());
+
+        DomainSelectionConnection.DomainSelectionConnectionCallback callback =
+                callbackCaptor.getValue();
+
+        assertNotNull(callback);
+
+        callback.onSelectionTerminated(ERROR_UNSPECIFIED);
+
+        verify(mEmergencyCallDomainSelectionConnection).cancelSelection();
+        verify(mEmergencyStateTracker).endCall(eq(TELECOM_CALL_ID1));
+    }
+
+    @Test
     public void testDomainSelectionLocalHangupStartEmergencyCall() throws Exception {
         setupForCallTest();
 
diff --git a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
index 7649f5a..673e586 100644
--- a/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/EmergencyCallDomainSelectorTest.java
@@ -38,6 +38,8 @@
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_VOWIFI_REQUIRES_CONDITION_INT;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_MAXIMUM_NUMBER_OF_EMERGENCY_TRIES_OVER_VOWIFI_INT;
 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_PREFER_IMS_EMERGENCY_WHEN_VOICE_CALLS_ON_CS_BOOL;
+import static android.telephony.CarrierConfigManager.ImsEmergency.SCAN_TYPE_FULL_SERVICE;
+import static android.telephony.CarrierConfigManager.ImsEmergency.SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE;
 import static android.telephony.CarrierConfigManager.ImsEmergency.SCAN_TYPE_NO_PREFERENCE;
 import static android.telephony.CarrierConfigManager.ImsEmergency.VOWIFI_REQUIRES_NONE;
 import static android.telephony.CarrierConfigManager.ImsEmergency.VOWIFI_REQUIRES_SETTING_ENABLED;
@@ -1167,6 +1169,67 @@
     }
 
     @Test
+    public void testFullService() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT, SCAN_TYPE_FULL_SERVICE);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+        mResultConsumer = null;
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(true);
+
+        EmergencyRegResult regResult = getEmergencyRegResult(UNKNOWN, REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), eq(DomainSelectionService.SCAN_TYPE_FULL_SERVICE), any(), any());
+        assertNotNull(mResultConsumer);
+
+        mResultConsumer.accept(regResult);
+        processAllMessages();
+
+        verify(mWwanSelectorCallback, times(2)).onRequestEmergencyNetworkScan(
+                any(), eq(DomainSelectionService.SCAN_TYPE_FULL_SERVICE), any(), any());
+    }
+
+    @Test
+    public void testFullServiceThenLimtedService() throws Exception {
+        PersistableBundle bundle = getDefaultPersistableBundle();
+        bundle.putInt(KEY_EMERGENCY_NETWORK_SCAN_TYPE_INT,
+                SCAN_TYPE_FULL_SERVICE_FOLLOWED_BY_LIMITED_SERVICE);
+        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(bundle);
+
+        mResultConsumer = null;
+        createSelector(SLOT_0_SUB_ID);
+        unsolBarringInfoChanged(true);
+
+        EmergencyRegResult regResult = getEmergencyRegResult(UNKNOWN, REGISTRATION_STATE_UNKNOWN,
+                0, false, false, 0, 0, "", "");
+        SelectionAttributes attr = getSelectionAttributes(SLOT_0, SLOT_0_SUB_ID, regResult);
+        mDomainSelector.selectDomain(attr, mTransportSelectorCallback);
+        processAllMessages();
+
+        bindImsServiceUnregistered();
+        processAllMessages();
+
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), eq(DomainSelectionService.SCAN_TYPE_FULL_SERVICE), any(), any());
+        assertNotNull(mResultConsumer);
+
+        mResultConsumer.accept(regResult);
+        processAllMessages();
+
+        verify(mWwanSelectorCallback, times(1)).onRequestEmergencyNetworkScan(
+                any(), eq(DomainSelectionService.SCAN_TYPE_LIMITED_SERVICE), any(), any());
+    }
+
+    @Test
     public void testCsThenPsPreference() throws Exception {
         PersistableBundle bundle = getDefaultPersistableBundle();
         int[] domainPreference = new int[] {