Merge "Add implementation of satellite provision APIs"
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index cec3a94..b8e429f 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -51,8 +51,10 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.ICancellationSignal;
 import android.os.LocaleList;
 import android.os.Looper;
 import android.os.Message;
@@ -143,7 +145,7 @@
 import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.stub.ImsConfigImplBase;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
-import android.telephony.satellite.ISatellitePositionUpdateCallback;
+import android.telephony.satellite.ISatelliteStateListener;
 import android.telephony.satellite.PointingInfo;
 import android.telephony.satellite.SatelliteManager;
 import android.text.TextUtils;
@@ -171,6 +173,7 @@
 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;
@@ -387,6 +390,13 @@
     private static final int EVENT_STOP_SATELLITE_POSITION_UPDATES_DONE = 122;
     private static final int CMD_GET_MAX_CHAR_PER_SATELLITE_TEXT_MSG = 123;
     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;
+
     // Parameters of select command.
     private static final int SELECT_COMMAND = 0xA4;
     private static final int SELECT_P1 = 0x04;
@@ -417,9 +427,20 @@
     private static final int USER_ACTIVITY_NOTIFICATION_DELAY = 200;
 
     private Set<Integer> mCarrierPrivilegeTestOverrideSubIds = new ArraySet<>();
-    private Map<Integer, SatellitePositionUpdateHandler> mSatellitePositionUpdateHandlers =
+    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 =
             new ConcurrentHashMap<>();
 
+    /**
+     * Map key: subId, value: SatelliteProvisionStateChangedHandler to notify registrants.
+     */
+    private ConcurrentHashMap<Integer, SatelliteProvisionStateChangedHandler>
+            mSatelliteProvisionStateChangedHandlers = 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_";
@@ -514,13 +535,25 @@
         }
     }
 
+    private static final class ProvisionSatelliteServiceArgument {
+        public @NonNull int[] features;
+        public @NonNull IIntegerConsumer callback;
+        public int subId;
+
+        ProvisionSatelliteServiceArgument(int[] features, IIntegerConsumer callback, int subId) {
+            this.features = features;
+            this.callback = callback;
+            this.subId = subId;
+        }
+    }
+
     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;
 
-        private final ISatellitePositionUpdateCallback mCallback;
+        private final ISatelliteStateListener mCallback;
 
-        SatellitePositionUpdateHandler(ISatellitePositionUpdateCallback callback, Looper looper) {
+        SatellitePositionUpdateHandler(ISatelliteStateListener callback, Looper looper) {
             super(looper);
             mCallback = callback;
         }
@@ -554,6 +587,66 @@
         }
     }
 
+    private static final class SatelliteProvisionStateChangedHandler extends Handler {
+        public static final int EVENT_PROVISION_STATE_CHANGED = 1;
+
+        private ConcurrentHashMap<IBinder, ISatelliteStateListener> mListeners;
+        private final int mSubId;
+
+        SatelliteProvisionStateChangedHandler(Looper looper, int subId) {
+            super(looper);
+            mListeners = new ConcurrentHashMap<>();
+            mSubId = subId;
+        }
+
+        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_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);
+                        } catch (RemoteException e) {
+                            log("EVENT_PROVISION_STATE_CHANGED RemoteException: " + e);
+                        }
+                    });
+
+                    setSatelliteEnabled(provisioned);
+                    /**
+                     * TODO: Take bugreport if provisioned is true and user did not initiate the
+                     * provision procedure for the corresponding subscription.
+                     */
+                    break;
+                }
+                default:
+                    loge("SatelliteProvisionStateChangedHandler unknown event: " + msg.what);
+            }
+        }
+
+        private void setSatelliteEnabled(boolean isEnabled) {
+            if (mSubId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+                SubscriptionManager.setSubscriptionProperty(
+                        mSubId, SubscriptionManager.SATELLITE_ENABLED, isEnabled ? "1" : "0");
+            } else {
+                //TODO (b/267826133): set enabled via SatelliteController.
+            }
+        }
+    }
+
     /**
      * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
      * request after sending. The main thread will notify the request when it is complete.
@@ -879,7 +972,7 @@
                     if (uiccPort == null) {
                         loge("iccCloseLogicalChannel: No UICC");
                         request.result = new IllegalArgumentException(
-                                    "iccCloseLogicalChannel: No UICC");
+                                "iccCloseLogicalChannel: No UICC");
                         notifyRequester(request);
                     } else {
                         onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE, request);
@@ -1447,7 +1540,7 @@
                         int errorCode = CellNetworkScanResult.STATUS_UNKNOWN_ERROR;
                         if (ar.exception instanceof CommandException) {
                             CommandException.Error error =
-                                ((CommandException) (ar.exception)).getCommandError();
+                                    ((CommandException) (ar.exception)).getCommandError();
                             if (error == CommandException.Error.RADIO_NOT_AVAILABLE) {
                                 errorCode = CellNetworkScanResult.STATUS_RADIO_NOT_AVAILABLE;
                             } else if (error == CommandException.Error.GENERIC_FAILURE) {
@@ -1604,7 +1697,7 @@
                         request.result = ar.result;
                     } else {
                         request.result = new IllegalStateException(
-                            "Failed to get carrier restrictions");
+                                "Failed to get carrier restrictions");
                         if (ar.result == null) {
                             loge("getAllowedCarriers: Empty response");
                         } else if (ar.exception instanceof CommandException) {
@@ -1635,8 +1728,8 @@
                             lockStatus = carrierRestrictionRules.getCarrierRestrictionStatus();
                             if (carrierId != -1 && callerCarrierId == carrierId && lockStatus
                                     == TelephonyManager.CARRIER_RESTRICTION_STATUS_RESTRICTED) {
-                                lockStatus =
-                                 TelephonyManager.CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER;
+                                lockStatus = TelephonyManager
+                                        .CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER;
                             }
                         } else {
                             Rlog.e(LOG_TAG,
@@ -1690,7 +1783,7 @@
                     }
                     onCompleted = obtainMessage(EVENT_GET_FORBIDDEN_PLMNS_DONE, request);
                     ((SIMRecords) uiccApp.getIccRecords()).getForbiddenPlmns(
-                              onCompleted);
+                            onCompleted);
                     break;
 
                 case CMD_SWITCH_SLOTS:
@@ -2104,7 +2197,7 @@
 
                         if (error == CommandException.Error.RADIO_NOT_AVAILABLE) {
                             request.result = TelephonyManager
-                                .THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
+                                    .THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
                         } else if (error == CommandException.Error.INVALID_ARGUMENTS) {
                             request.result = SET_DATA_THROTTLING_MODEM_THREW_INVALID_PARAMS;
                         } else if (error == CommandException.Error.REQUEST_NOT_SUPPORTED) {
@@ -2183,8 +2276,8 @@
                     onCompleted = obtainMessage(EVENT_SET_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE,
                             request);
                     phone.getSignalStrengthController().setSignalStrengthUpdateRequest(
-                                    request.subId, pair.first /*callingUid*/,
-                                    pair.second /*request*/, onCompleted);
+                            request.subId, pair.first /*callingUid*/,
+                            pair.second /*request*/, onCompleted);
                     break;
                 }
                 case EVENT_SET_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE: {
@@ -2211,8 +2304,8 @@
                     onCompleted = obtainMessage(EVENT_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE,
                             request);
                     phone.getSignalStrengthController().clearSignalStrengthUpdateRequest(
-                                    request.subId, pair.first /*callingUid*/,
-                                    pair.second /*request*/, onCompleted);
+                            request.subId, pair.first /*callingUid*/,
+                            pair.second /*request*/, onCompleted);
                     break;
                 }
                 case EVENT_CLEAR_SIGNAL_STRENGTH_UPDATE_REQUEST_DONE: {
@@ -2296,7 +2389,7 @@
                     request = (MainThreadRequest) msg.obj;
                     request.result =
                             UiccController.getInstance().getPinStorage()
-                                .prepareUnattendedReboot(request.workSource);
+                                    .prepareUnattendedReboot(request.workSource);
                     notifyRequester(request);
                     break;
 
@@ -2310,7 +2403,7 @@
                     } else {
                         loge("startSatellitePositionUpdates: No phone object");
                         request.result =
-                                SatelliteManager.SATELLITE_SERVICE_TELEPHONY_INTERNAL_ERROR;
+                                SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
                         notifyRequester(request);
                     }
                     break;
@@ -2346,7 +2439,7 @@
                     } else {
                         loge("stopSatellitePositionUpdates: No phone object");
                         request.result =
-                                SatelliteManager.SATELLITE_SERVICE_TELEPHONY_INTERNAL_ERROR;
+                                SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
                         notifyRequester(request);
                     }
                     break;
@@ -2382,7 +2475,7 @@
                     } else {
                         loge("getMaxCharactersPerSatelliteTextMessage: No phone object");
                         request.result = SatelliteManager
-                                .SATELLITE_SERVICE_TELEPHONY_INTERNAL_ERROR;
+                                .SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
                         notifyRequester(request);
                     }
                     break;
@@ -2406,7 +2499,7 @@
                         }
                     } else if (ar.result == null) {
                         request.result = SatelliteManager
-                                .SATELLITE_SERVICE_TELEPHONY_INTERNAL_ERROR;
+                                .SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
                         loge("getMaxCharactersPerSatelliteTextMessage: result is null");
                     } else {
                         request.result = SatelliteManager.SATELLITE_SERVICE_SUCCESS;
@@ -2419,6 +2512,136 @@
                     break;
                 }
 
+                case CMD_PROVISION_SATELLITE_SERVICE: {
+                    request = (MainThreadRequest) msg.obj;
+                    onCompleted =
+                            obtainMessage(EVENT_PROVISION_SATELLITE_SERVICE_DONE, request);
+                    Phone phone = getPhoneFromRequest(request);
+                    if (phone != null) {
+                        handleCmdProvisionSatelliteService(
+                                (ProvisionSatelliteServiceArgument) request.argument,
+                                phone, onCompleted);
+                    } else {
+                        loge("provisionSatelliteService: No phone object");
+                        request.result =
+                                SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
+                        notifyRequester(request);
+                    }
+                    break;
+                }
+
+                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);
+                    notifyRequester(request);
+                    break;
+                }
+
+                case CMD_CANCEL_PROVISION_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);
+                    Phone phone = getPhoneFromRequest(request);
+                    if (phone != null) {
+                        phone.getProvisionedSatelliteFeatures(onCompleted);
+                    } else {
+                        loge("getProvisionedSatelliteFeatures: No phone object");
+                        request.result = SatelliteManager
+                                .SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
+                        notifyRequester(request);
+                    }
+                    break;
+                }
+
+                case EVENT_GET_PROVISIONED_SATELLITE_FEATURES_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");
+                        }
+                    }
+                    notifyRequester(request);
+                    break;
+                }
+
                 default:
                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
                     break;
@@ -2867,14 +3090,16 @@
                                 synchronized (UnlockSim.this) {
                                     mRetryCount = msg.arg1;
                                     if (ar.exception != null) {
-                                        if (ar.exception instanceof CommandException &&
-                                                ((CommandException)(ar.exception)).getCommandError()
-                                                == CommandException.Error.PASSWORD_INCORRECT) {
+                                        CommandException.Error error = null;
+                                        if (ar.exception instanceof CommandException) {
+                                            error = ((CommandException) (ar.exception))
+                                                    .getCommandError();
+                                        }
+                                        if (error == CommandException.Error.PASSWORD_INCORRECT) {
                                             mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
-                                        } //When UiccCardApp dispose,handle message and return exception
-                                          else if (ar.exception instanceof CommandException &&
-                                                ((CommandException) (ar.exception)).getCommandError()
-                                                        == CommandException.Error.ABORTED) {
+                                        } else if (error == CommandException.Error.ABORTED) {
+                                            /* When UiccCardApp dispose, handle message and return
+                                             exception */
                                             mResult = PhoneConstants.PIN_OPERATION_ABORTED;
                                         } else {
                                             mResult = PhoneConstants.PIN_GENERAL_FAILURE;
@@ -4404,8 +4629,8 @@
     }
 
     /**
-      * returns true, if the device is in a state where both voice and data
-      * are supported simultaneously. This can change based on location or network condition.
+     * returns true, if the device is in a state where both voice and data
+     * are supported simultaneously. This can change based on location or network condition.
      */
     @Override
     public boolean isConcurrentVoiceAndDataAllowed(int subId) {
@@ -4445,8 +4670,8 @@
     @Override
     public int getNetworkSelectionMode(int subId) {
         TelephonyPermissions
-                    .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
-                    mApp, subId, "getNetworkSelectionMode");
+                .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                        mApp, subId, "getNetworkSelectionMode");
         final long identity = Binder.clearCallingIdentity();
         try {
             if (!isActiveSubscription(subId)) {
@@ -5484,7 +5709,7 @@
             return getDataNetworkTypeForSubscriber(subId, callingPackage, callingFeatureId);
         } else if (targetSdk == android.os.Build.VERSION_CODES.Q
                 && !TelephonyPermissions.checkCallingOrSelfReadPhoneStateNoThrow(
-                        mApp, subId, callingPackage, callingFeatureId,
+                mApp, subId, callingPackage, callingFeatureId,
                 "getNetworkTypeForSubscriber")) {
             return TelephonyManager.NETWORK_TYPE_UNKNOWN;
         }
@@ -5694,7 +5919,7 @@
                 portIndex);
         if (phoneId == -1) {
             throw new IllegalArgumentException("Given slot index: " + slotIndex + " port index: "
-                     + portIndex + " does not correspond to an active phone");
+                    + portIndex + " does not correspond to an active phone");
         }
         return PhoneFactory.getPhone(phoneId);
     }
@@ -5813,12 +6038,12 @@
 
     @Override
     public String iccTransmitApduLogicalChannelByPort(int slotIndex, int portIndex, int channel,
-             int cla, int command, int p1, int p2, int p3, String data) {
+            int cla, int command, int p1, int p2, int p3, String data) {
         enforceModifyPermission();
         if (DBG) {
             log("iccTransmitApduLogicalChannelByPort: slotIndex=" + slotIndex + " portIndex="
-                     + portIndex + " chnl=" + channel + " cla=" + cla + " cmd=" + command + " p1="
-                     + p1 + " p2=" + p2 + " p3=" + p3 + " data=" + data);
+                    + portIndex + " chnl=" + channel + " cla=" + cla + " cmd=" + command + " p1="
+                    + p1 + " p2=" + p2 + " p3=" + p3 + " data=" + data);
         }
         return iccTransmitApduLogicalChannelWithPermission(
                 getPhoneFromSlotPortIndexOrThrowException(slotIndex, portIndex), channel, cla,
@@ -5866,13 +6091,13 @@
 
     @Override
     public String iccTransmitApduBasicChannelByPort(int slotIndex, int portIndex,
-             String callingPackage, int cla, int command, int p1, int p2, int p3, String data) {
+            String callingPackage, int cla, int command, int p1, int p2, int p3, String data) {
         enforceModifyPermission();
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         if (DBG) {
             log("iccTransmitApduBasicChannelByPort: slotIndex=" + slotIndex + " portIndex="
-                     + portIndex + " cla=" + cla + " cmd=" + command + " p1=" + p1 + " p2="
-                     + p2 + " p3=" + p3 + " data=" + data);
+                    + portIndex + " cla=" + cla + " cmd=" + command + " p1=" + p1 + " p2="
+                    + p2 + " p3=" + p3 + " data=" + data);
         }
 
         return iccTransmitApduBasicChannelWithPermission(
@@ -6508,7 +6733,7 @@
             Binder.restoreCallingIdentity(identity);
         }
     }
-     /**
+    /**
      * Get the manual network selection
      *
      * @param subId the id of the subscription.
@@ -6518,8 +6743,8 @@
     @Override
     public String getManualNetworkSelectionPlmn(int subId) {
         TelephonyPermissions
-                    .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
-                    mApp, subId, "getManualNetworkSelectionPlmn");
+                .enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+                        mApp, subId, "getManualNetworkSelectionPlmn");
 
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -6533,7 +6758,7 @@
             }
             OperatorInfo networkSelection = phone.getSavedNetworkSelection();
             return TextUtils.isEmpty(networkSelection.getOperatorNumeric())
-                ? phone.getManualNetworkSelectionPlmn() : networkSelection.getOperatorNumeric();
+                    ? phone.getManualNetworkSelectionPlmn() : networkSelection.getOperatorNumeric();
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -6691,7 +6916,7 @@
                 String newUssdCommand = "";
                 try {
                     newUssdCommand = carrierXmlParser.getFeature(
-                            CarrierXmlParser.FEATURE_CALL_WAITING)
+                                    CarrierXmlParser.FEATURE_CALL_WAITING)
                             .makeCommand(CarrierXmlParser.SsEntry.SSAction.QUERY, null);
                 } catch (NullPointerException e) {
                     loge("Failed to generate USSD number" + e);
@@ -6748,7 +6973,7 @@
                 String newUssdCommand = "";
                 try {
                     newUssdCommand = carrierXmlParser.getFeature(
-                            CarrierXmlParser.FEATURE_CALL_WAITING)
+                                    CarrierXmlParser.FEATURE_CALL_WAITING)
                             .makeCommand(ssAction, null);
                 } catch (NullPointerException e) {
                     loge("Failed to generate USSD number" + e);
@@ -6792,16 +7017,16 @@
                 LocationAccessPolicy.LocationPermissionResult.DENIED_HARD;
         if (!renounceFineLocationAccess) {
             locationResult = LocationAccessPolicy.checkLocationPermission(mApp,
-                            new LocationAccessPolicy.LocationPermissionQuery.Builder()
-                                    .setCallingPackage(callingPackage)
-                                    .setCallingFeatureId(callingFeatureId)
-                                    .setCallingPid(Binder.getCallingPid())
-                                    .setCallingUid(Binder.getCallingUid())
-                                    .setMethod("requestNetworkScan")
-                                    .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
-                                    .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q)
-                                    .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q)
-                                    .build());
+                    new LocationAccessPolicy.LocationPermissionQuery.Builder()
+                            .setCallingPackage(callingPackage)
+                            .setCallingFeatureId(callingFeatureId)
+                            .setCallingPid(Binder.getCallingPid())
+                            .setCallingUid(Binder.getCallingUid())
+                            .setMethod("requestNetworkScan")
+                            .setMinSdkVersionForFine(Build.VERSION_CODES.Q)
+                            .setMinSdkVersionForCoarse(Build.VERSION_CODES.Q)
+                            .setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q)
+                            .build());
         }
         if (locationResult != LocationAccessPolicy.LocationPermissionResult.ALLOWED) {
             SecurityException e = checkNetworkRequestForSanitizedLocationAccess(
@@ -6839,7 +7064,7 @@
         }
         boolean hasNetworkScanPermission =
                 mApp.checkCallingOrSelfPermission(android.Manifest.permission.NETWORK_SCAN)
-                == PERMISSION_GRANTED;
+                        == PERMISSION_GRANTED;
 
         if (!hasCarrierPriv && !hasNetworkScanPermission) {
             return new SecurityException("permission.NETWORK_SCAN or carrier privileges is needed"
@@ -8051,9 +8276,10 @@
     public @Nullable PhoneAccountHandle getPhoneAccountHandleForSubscriptionId(int subscriptionId) {
         TelephonyPermissions
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
-                mApp,
-                subscriptionId,
-                "getPhoneAccountHandleForSubscriptionId, " + "subscriptionId: " + subscriptionId);
+                        mApp,
+                        subscriptionId,
+                        "getPhoneAccountHandleForSubscriptionId, " + "subscriptionId: "
+                                + subscriptionId);
         final long identity = Binder.clearCallingIdentity();
         try {
             Phone phone = getPhone(subscriptionId);
@@ -8313,8 +8539,8 @@
         int totalTxTimeMs = Arrays.stream(info.getTransmitTimeMillis()).sum();
 
         return (info.isValid()
-            && (info.getSleepTimeMillis() <= activityDurationMs)
-            && (info.getIdleTimeMillis() <= activityDurationMs));
+                && (info.getSleepTimeMillis() <= activityDurationMs)
+                && (info.getIdleTimeMillis() <= activityDurationMs));
     }
 
     private void updateLastModemActivityInfo(ModemActivityInfo info, int rat, int freq) {
@@ -8348,10 +8574,10 @@
                 info.getReceiveTimeMillis(rat) + mLastModemActivityInfo.getReceiveTimeMillis(rat));
     }
 
-   /**
-    * Merge this ModemActivityInfo with mLastModemActivitySpecificInfo
-    * @param info recent ModemActivityInfo
-    */
+    /**
+     * Merge this ModemActivityInfo with mLastModemActivitySpecificInfo
+     * @param info recent ModemActivityInfo
+     */
     private void mergeModemActivityInfo(ModemActivityInfo info) {
         List<ActivityStatsTechSpecificInfo> merged = new ArrayList<>();
         ActivityStatsTechSpecificInfo deltaSpecificInfo;
@@ -8396,17 +8622,17 @@
         mLastModemActivityInfo.setTimestamp(info.getTimestampMillis());
         mLastModemActivityInfo.setSleepTimeMillis(
                 info.getSleepTimeMillis()
-                + mLastModemActivityInfo.getSleepTimeMillis());
+                        + mLastModemActivityInfo.getSleepTimeMillis());
         mLastModemActivityInfo.setIdleTimeMillis(
                 info.getIdleTimeMillis()
-                + mLastModemActivityInfo.getIdleTimeMillis());
+                        + mLastModemActivityInfo.getIdleTimeMillis());
 
         mLastModemActivityInfo =
-                 new ModemActivityInfo(
-                         mLastModemActivityInfo.getTimestampMillis(),
-                         mLastModemActivityInfo.getSleepTimeMillis(),
-                         mLastModemActivityInfo.getIdleTimeMillis(),
-                         mLastModemActivitySpecificInfo);
+                new ModemActivityInfo(
+                        mLastModemActivityInfo.getTimestampMillis(),
+                        mLastModemActivityInfo.getSleepTimeMillis(),
+                        mLastModemActivityInfo.getIdleTimeMillis(),
+                        mLastModemActivitySpecificInfo);
     }
 
     private ActivityStatsTechSpecificInfo[] deepCopyModemActivitySpecificInfo(
@@ -9028,7 +9254,7 @@
             @NonNull String[] args) {
         return new TelephonyShellCommand(this, getDefaultPhone().getContext()).exec(
                 this, in.getFileDescriptor(), out.getFileDescriptor(),
-                        err.getFileDescriptor(), args);
+                err.getFileDescriptor(), args);
     }
 
     /**
@@ -9324,7 +9550,7 @@
     public boolean isManualNetworkSelectionAllowed(int subId) {
         TelephonyPermissions
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
-                mApp, subId, "isManualNetworkSelectionAllowed");
+                        mApp, subId, "isManualNetworkSelectionAllowed");
 
         boolean isAllowed = true;
         final long identity = Binder.clearCallingIdentity();
@@ -9368,7 +9594,7 @@
             // even without READ_PRIVILEGED_PHONE_STATE, we allow the call to continue if the caller
             // has carrier privileges on an active UICC
             if (checkCarrierPrivilegesForPackageAnyPhoneWithPermission(callingPackage)
-                        != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+                    != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
                 throw new SecurityException("Caller does not have permission.");
             }
         }
@@ -9753,7 +9979,7 @@
     public int getCdmaRoamingMode(int subId) {
         TelephonyPermissions
                 .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
-                mApp, subId, "getCdmaRoamingMode");
+                        mApp, subId, "getCdmaRoamingMode");
 
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -9843,7 +10069,7 @@
                 // TelephonyManager#isPotentialEmergencyNumber is removed completely
                 if (phone.getEmergencyNumberTracker() != null
                         && phone.getEmergencyNumberTracker()
-                                .isEmergencyNumber(number)) {
+                        .isEmergencyNumber(number)) {
                     return true;
                 }
             }
@@ -10717,7 +10943,7 @@
         try {
             getGbaManager(subId).bootstrapAuthenticationRequest(
                     new GbaAuthRequest(subId, appType, nafUrl, securityProtocol.toByteArray(),
-                    forceBootStrapping, callback));
+                            forceBootStrapping, callback));
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -10768,7 +10994,7 @@
 
         if (isDataThrottlingSupported) {
             int thermalMitigationResult =
-                (int) sendRequest(CMD_SET_DATA_THROTTLING, dataThrottlingRequest, subId);
+                    (int) sendRequest(CMD_SET_DATA_THROTTLING, dataThrottlingRequest, subId);
             if (thermalMitigationResult == SET_DATA_THROTTLING_MODEM_THREW_INVALID_PARAMS) {
                 throw new IllegalArgumentException("modem returned INVALID_ARGUMENTS");
             } else if (thermalMitigationResult
@@ -10803,8 +11029,8 @@
         for (Phone phone : PhoneFactory.getPhones()) {
             if (phone.isInEmergencySmsMode() || phone.isInEcm()) {
                 Log.e(LOG_TAG, "Phone state is not valid. isInEmergencySmsMode = "
-                    + phone.isInEmergencySmsMode() + " isInEmergencyCallbackMode = "
-                    + phone.isInEcm());
+                        + phone.isInEmergencySmsMode() + " isInEmergencyCallbackMode = "
+                        + phone.isInEcm());
                 return true;
             }
         }
@@ -10865,9 +11091,9 @@
             switch (thermalMitigationAction) {
                 case ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_DATA_THROTTLING:
                     thermalMitigationResult =
-                        handleDataThrottlingRequest(subId,
-                                thermalMitigationRequest.getDataThrottlingRequest(),
-                                callingPackage);
+                            handleDataThrottlingRequest(subId,
+                                    thermalMitigationRequest.getDataThrottlingRequest(),
+                                    callingPackage);
                     break;
                 case ThermalMitigationRequest.THERMAL_MITIGATION_ACTION_VOICE_ONLY:
                     if (thermalMitigationRequest.getDataThrottlingRequest() != null) {
@@ -10898,7 +11124,7 @@
                         Phone phone = getPhone(subId);
                         if (phone == null) {
                             thermalMitigationResult =
-                                TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
+                                    TelephonyManager.THERMAL_MITIGATION_RESULT_MODEM_NOT_AVAILABLE;
                             break;
                         }
 
@@ -10911,7 +11137,7 @@
                             break;
                         } else if (isAnyPhoneInEmergencyState()) {
                             thermalMitigationResult =
-                                TelephonyManager.THERMAL_MITIGATION_RESULT_INVALID_STATE;
+                                    TelephonyManager.THERMAL_MITIGATION_RESULT_INVALID_STATE;
                             break;
                         }
                     } else {
@@ -10928,7 +11154,7 @@
                         break;
                     }
                     thermalMitigationResult =
-                        TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS;
+                            TelephonyManager.THERMAL_MITIGATION_RESULT_SUCCESS;
                     break;
                 default:
                     throw new IllegalArgumentException("the requested thermalMitigationAction does "
@@ -11969,8 +12195,8 @@
         Log.d(LOG_TAG, "setModemService - " + serviceName);
         TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setModemService");
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
-                        SubscriptionManager.INVALID_SUBSCRIPTION_ID,
-                        "setModemService");
+                SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+                "setModemService");
         return mPhoneConfigurationManager.setModemService(serviceName);
     }
 
@@ -11984,7 +12210,7 @@
         Log.d(LOG_TAG, "getModemService");
         TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "getModemService");
         TelephonyPermissions
-                        .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                .enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
                         mApp, SubscriptionManager.INVALID_SUBSCRIPTION_ID,
                         "getModemService");
         result = mPhoneConfigurationManager.getModemService();
@@ -12232,34 +12458,28 @@
      * 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 callbackId The callback ID associating the public SatellitePositionUpdateCallback to
-     *                   the internal ISatellitePositionUpdateCallback below.
      * @param callback The callback to notify of changes in satellite position.
      * @return The result of the operation.
      */
     @Override
     @SatelliteManager.SatelliteServiceResult public int startSatellitePositionUpdates(int subId,
-            int callbackId, @NonNull ISatellitePositionUpdateCallback callback) {
+            @NonNull ISatelliteStateListener callback) {
         enforceSatelliteCommunicationPermission("startSatellitePositionUpdates");
 
-        if (!isSatelliteEnabled(subId)) {
+        final int validSubId = getValidSatelliteSubId(subId);
+        if (!isSatelliteEnabled(validSubId)) {
             return SatelliteManager.SATELLITE_SERVICE_DISABLED;
         }
 
-        Phone phone = getPhone(subId);
+        Phone phone = getPhoneOrDefault(validSubId, "startSatellitePositionUpdates");
         if (phone == null) {
-            loge("startSatellitePositionUpdates called with invalid subId: " + subId
-                    + ". Retrying with default phone.");
-            phone = getDefaultPhone();
-            if (phone == null) {
-                loge("startSatellitePositionUpdates failed with no phone object.");
-                return SatelliteManager.SATELLITE_SERVICE_TELEPHONY_INTERNAL_ERROR;
-            }
+            return SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
         }
 
-        if (mSatellitePositionUpdateHandlers.containsKey(callbackId)) {
-            log("startSatellitePositionUpdates: callback already registered: " + callbackId);
-            return SatelliteManager.SATELLITE_SERVICE_SUCCESS;
+        if (mSatellitePositionUpdateHandlers.containsKey(callback.asBinder())) {
+            log("startSatellitePositionUpdates: callback already registered: "
+                    + callback.asBinder());
+            return SatelliteManager.SATELLITE_SERVICE_CALLBACK_ALREADY_REGISTERED;
         }
 
         SatellitePositionUpdateHandler handler =
@@ -12268,9 +12488,9 @@
                 SatellitePositionUpdateHandler.EVENT_POSITION_UPDATE, null);
         phone.registerForSatelliteMessagesTransferComplete(handler,
                 SatellitePositionUpdateHandler.EVENT_MESSAGE_TRANSFER_STATE_UPDATE, null);
-        mSatellitePositionUpdateHandlers.put(callbackId, handler);
+        mSatellitePositionUpdateHandlers.put(callback.asBinder(), handler);
 
-        int result = (int) sendRequest(CMD_START_SATELLITE_POSITION_UPDATES, null, subId);
+        int result = (int) sendRequest(CMD_START_SATELLITE_POSITION_UPDATES, null, validSubId);
         if (DBG) log("startSatellitePositionUpdates result: " + result);
         return result;
     }
@@ -12280,35 +12500,30 @@
      * 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 callbackId The ID of the callback that was passed in {@link
-     *                   #startSatellitePositionUpdates(int, int, ISatellitePositionUpdateCallback)}
+     * @param callback The callback that was passed in {@link
+     *                   #startSatellitePositionUpdates(int, ISatelliteStateListener)}
      * @return The result of the operation.
      */
     @Override
     @SatelliteManager.SatelliteServiceResult public int stopSatellitePositionUpdates(int subId,
-            int callbackId) {
+            @NonNull ISatelliteStateListener callback) {
         enforceSatelliteCommunicationPermission("stopSatellitePositionUpdates");
 
-        if (!isSatelliteEnabled(subId)) {
+        final int validSubId = getValidSatelliteSubId(subId);
+        if (!isSatelliteEnabled(validSubId)) {
             return SatelliteManager.SATELLITE_SERVICE_DISABLED;
         }
 
-        Phone phone = getPhone(subId);
+        Phone phone = getPhoneOrDefault(validSubId, "stopSatellitePositionUpdates");
         if (phone == null) {
-            loge("stopSatellitePositionUpdates called with invalid subId: " + subId
-                    + ". Retrying with default phone.");
-            phone = getDefaultPhone();
-            if (phone == null) {
-                loge("stopSatellitePositionUpdates failed with no phone object.");
-                return SatelliteManager.SATELLITE_SERVICE_TELEPHONY_INTERNAL_ERROR;
-            }
+            return SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
         }
 
         SatellitePositionUpdateHandler handler =
-                mSatellitePositionUpdateHandlers.remove(callbackId);
+                mSatellitePositionUpdateHandlers.remove(callback.asBinder());
         if (handler == null) {
             loge("stopSatellitePositionUpdates: No SatellitePositionArgument");
-            return SatelliteManager.SATELLITE_SERVICE_TELEPHONY_INTERNAL_ERROR;
+            return SatelliteManager.SATELLITE_SERVICE_CALLBACK_NOT_REGISTERED;
         } else {
             phone.unregisterForSatellitePointingInfoChanged(handler);
             phone.unregisterForSatelliteMessagesTransferComplete(handler);
@@ -12319,7 +12534,7 @@
             return SatelliteManager.SATELLITE_SERVICE_SUCCESS;
         }
 
-        int result = (int) sendRequest(CMD_STOP_SATELLITE_POSITION_UPDATES, null, subId);
+        int result = (int) sendRequest(CMD_STOP_SATELLITE_POSITION_UPDATES, null, validSubId);
         if (DBG) log("stopSatellitePositionUpdates result: " + result);
         return result;
     }
@@ -12337,28 +12552,333 @@
     public int getMaxCharactersPerSatelliteTextMessage(int subId, IIntegerConsumer callback) {
         enforceSatelliteCommunicationPermission("getMaxCharactersPerSatelliteTextMessage");
 
-        if (!isSatelliteEnabled(subId)) {
+        final int validSubId = getValidSatelliteSubId(subId);
+        if (!isSatelliteEnabled(validSubId)) {
             return SatelliteManager.SATELLITE_SERVICE_DISABLED;
         }
 
-        Phone phone = getPhone(subId);
+        Phone phone = getPhoneOrDefault(validSubId, "getMaxCharactersPerSatelliteTextMessage");
         if (phone == null) {
-            loge("getMaxCharactersPerSatelliteTextMessage called with invalid subId: " + subId
-                    + ".Retrying with default phone.");
-            phone = getDefaultPhone();
-            if (phone == null) {
-                loge("getMaxCharactersPerSatelliteTextMessage failed with no phone object.");
-                return SatelliteManager.SATELLITE_SERVICE_TELEPHONY_INTERNAL_ERROR;
-            }
+            return SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
         }
 
         Consumer<Integer> argument = FunctionalUtils.ignoreRemoteException(callback::accept);
-        int result = (int) sendRequest(CMD_GET_MAX_CHAR_PER_SATELLITE_TEXT_MSG, argument, subId);
+        int result = (int) sendRequest(
+                CMD_GET_MAX_CHAR_PER_SATELLITE_TEXT_MSG, argument, validSubId);
         if (DBG) log("getMaxCharPerTextMessageOnSatellite result: " + result);
         return result;
     }
 
     /**
+     * Register the subscription with a satellite provider.
+     * 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 callback The callback to get the error code of the request.
+     * @return The signal transport used by the caller to cancel the provision request.
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    public ICancellationSignal provisionSatelliteService(int subId, int[] features,
+            IIntegerConsumer callback) {
+        enforceSatelliteCommunicationPermission("provisionSatelliteService");
+
+        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);
+            return null;
+        }
+
+        if (mSatelliteProvisionCallbacks.containsKey(validSubId)) {
+            sendResponse(callback, SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS,
+                    callbackRemoteExErrorMessage);
+            return null;
+        }
+
+        if (isSatelliteEnabled(validSubId)) {
+            sendResponse(callback, SatelliteManager.SATELLITE_SERVICE_ALREADY_PROVISIONED,
+                    callbackRemoteExErrorMessage);
+            return null;
+        }
+
+        sendRequestAsync(CMD_PROVISION_SATELLITE_SERVICE,
+                new ProvisionSatelliteServiceArgument(features, callback, validSubId), phone, null);
+
+        ICancellationSignal cancelTransport = CancellationSignal.createTransport();
+        CancellationSignal.fromTransport(cancelTransport)
+                .setOnCancelListener(() -> {
+                    sendRequestAsync(CMD_CANCEL_PROVISION_SATELLITE_SERVICE, validSubId);
+                });
+        return cancelTransport;
+    }
+
+    /**
+     * 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.
+     * @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;
+    }
+
+    /**
+     * 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)}
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    @SatelliteManager.SatelliteServiceResult
+    public int unregisterForSatelliteProvisionStateChanged(
+            int subId, ISatelliteStateListener callback) {
+        enforceSatelliteCommunicationPermission("unregisterForSatelliteProvisionStateChanged");
+
+        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_SERVICE_CALLBACK_NOT_REGISTERED;
+    }
+
+    /**
+     * Get the list of provisioned satellite features.
+     *
+     * @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.
+     * @throws SecurityException if the caller doesn't have the required permission.
+     */
+    @Override
+    @SatelliteManager.SatelliteServiceResult
+    public int getProvisionedSatelliteFeatures(int subId, IIntArrayConsumer callback) {
+        enforceSatelliteCommunicationPermission("getProvisionedSatelliteFeatures");
+
+        final int validSubId = getValidSatelliteSubId(subId);
+        if (!isSatelliteEnabled(validSubId)) {
+            return SatelliteManager.SATELLITE_SERVICE_DISABLED;
+        }
+
+        final Phone phone = getPhoneOrDefault(validSubId, "getProvisionedSatelliteFeatures");
+        if (phone == null) {
+            return SatelliteManager.SATELLITE_SERVICE_INVALID_TELEPHONY_STATE;
+        }
+
+        int result = (int) sendRequest(
+                CMD_GET_PROVISIONED_SATELLITE_FEATURES, callback, validSubId);
+        if (DBG) log("getProvisionedSatelliteFeatures result: " + result);
+        return result;
+    }
+
+    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);
+        } else {
+            sendResponse(arg.callback, SatelliteManager.SATELLITE_SERVICE_PROVISION_IN_PROGRESS,
+                    callbackRemoteExErrorMessage);
+        }
+    }
+
+    private void handleEventProvisionSatelliteServiceDone(MainThreadRequest request) {
+        final ProvisionSatelliteServiceArgument arg =
+                (ProvisionSatelliteServiceArgument) request.argument;
+        if (arg == null) {
+            loge("handleEventProvisionSatelliteServiceDone: arg is null");
+            return;
+        }
+
+        IIntegerConsumer callback = mSatelliteProvisionCallbacks.remove(arg.subId);
+        if (callback == null) {
+            loge("handleEventProvisionSatelliteServiceDone: callback is null for subId="
+                    + arg.subId);
+            return;
+        }
+
+        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);
+        }
+
+        /**
+         * We need to update satellite provision status in SubscriptionController
+         * or SatelliteController.
+         * TODO (b/267826133) we need to do this for all subscriptions on the device.
+         */
+        registerForSatelliteProvisionStateChanged(arg.subId, null);
+    }
+
+    private void handleCmdCancelProvisionSatelliteService(int subId, Message onCompleted) {
+        final Phone phone = getPhoneOrDefault(
+                subId, "handleCmdCancelProvisionSatelliteService");
+        if (phone == null) {
+            return;
+        }
+        phone.cancelProvisionSatelliteService(onCompleted, getSatelliteImsi(subId));
+    }
+
+    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 String getSatelliteImsi(int subId) {
+        if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+            return "";
+        }
+
+        if (mSubscriptionController == null) {
+            loge("getSatelliteImsi: mSubscriptionController is null");
+            return "";
+        }
+        return mSubscriptionController.getImsiPrivileged(subId);
+    }
+
+    private Phone getPhoneOrDefault(int subId, String caller) {
+        Phone phone = getPhone(subId);
+        if (phone == null) {
+            loge(caller + " called with invalid subId: " + subId
+                    + ". Retrying with default phone.");
+            phone = getDefaultPhone();
+            if (phone == null) {
+                loge(caller + " failed with no phone object.");
+            }
+        }
+        return phone;
+    }
+
+    /**
+     * Check if satellite is enabled for a subscription.
+     *
+     * 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) {
+        if (subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+            if (mSubscriptionController == null) {
+                loge("setSatelliteEnabled mSubscriptionController is null");
+                return false;
+            }
+
+            String strResult = mSubscriptionController.getSubscriptionProperty(
+                    subId, SubscriptionManager.SATELLITE_ENABLED);
+            if (strResult != null) {
+                int intResult = Integer.parseInt(strResult);
+                return (intResult == 1) ? true : false;
+            }
+        } else {
+            //TODO (b/267826133): check via SatelliteController
+        }
+        return false;
+    }
+
+    /**
+     * Set satellite enabled for a subscription.
+     *
+     * 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) {
+        if (subId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+            if (mSubscriptionController == null) {
+                loge("setSatelliteEnabled mSubscriptionController is null");
+                return;
+            }
+            mSubscriptionController.setSubscriptionProperty(
+                    subId, SubscriptionManager.SATELLITE_ENABLED, isEnabled ? "1" : "0");
+        } else {
+            //TODO (b/267826133): enable via SatelliteController
+        }
+    }
+
+    private int getValidSatelliteSubId(int subId) {
+        if (mSubscriptionController == null) {
+            loge("getValidSatelliteSubId mSubscriptionController is null. "
+                    + "Use DEFAULT_SUBSCRIPTION_ID for subId=" + subId);
+            return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+        }
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Context context = getDefaultPhone().getContext();
+            if (mSubscriptionController.isActiveSubId(
+                    subId, context.getOpPackageName(), context.getAttributionTag())) {
+                return subId;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        if (DBG) log("getValidSatelliteSubId: use DEFAULT_SUBSCRIPTION_ID for subId=" + subId);
+        return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+    }
+
+    /**
      * 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:
@@ -12383,21 +12903,6 @@
     }
 
     /**
-     * Check if satellite is enabled for a subscription.
-     */
-    private boolean isSatelliteEnabled(int subId) {
-        if (mSubscriptionController != null) {
-            String strResult = mSubscriptionController.getSubscriptionProperty(
-                    subId, SubscriptionManager.SATELLITE_ENABLED);
-            if (strResult != null) {
-                int intResult = Integer.parseInt(strResult);
-                return (intResult == 1) ? true : false;
-            }
-        }
-        return false;
-    }
-
-    /**
      * Class binds the consumer[callback] and carrierId.
      */
     private static class CallerCallbackInfo {