Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c66cc4bb authored by Hakjun Choi's avatar Hakjun Choi
Browse files

Clean up satellite provision APIS

1) report current status on callback registration
2) No need to have vendors implement provision related apis. We can remove these interfaces
3) store provision status in persistent memory in sim table

Bug: 348272056
Flag: com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn
Test: atest SatelliteControllerTest, SatelliteSOSMessageRecommenderTest,
SubscriptionDatabaseManagerTest, SubscriptionInfoInternalTest
Test: manual e2e test with several times of provision/deprovision then
check SIM DB

Change-Id: I7b57435345f4718b0a9e99946575903315a9efd5
parent e5c54483
Loading
Loading
Loading
Loading
+95 −125
Original line number Diff line number Diff line
@@ -218,7 +218,6 @@ public class SatelliteController extends Handler {
    private static final int EVENT_RADIO_STATE_CHANGED = 23;
    private static final int CMD_IS_SATELLITE_PROVISIONED = 24;
    private static final int EVENT_IS_SATELLITE_PROVISIONED_DONE = 25;
    private static final int EVENT_SATELLITE_PROVISION_STATE_CHANGED = 26;
    private static final int EVENT_PENDING_DATAGRAMS = 27;
    private static final int EVENT_SATELLITE_MODEM_STATE_CHANGED = 28;
    private static final int EVENT_SET_SATELLITE_PLMN_INFO_DONE = 29;
@@ -295,8 +294,6 @@ public class SatelliteController extends Handler {
    private boolean mWaitingForDisableSatelliteModemResponse = false;
    private boolean mWaitingForSatelliteModemOff = false;

    private final AtomicBoolean mRegisteredForProvisionStateChangedWithSatelliteService =
            new AtomicBoolean(false);
    private final AtomicBoolean mRegisteredForPendingDatagramCountWithSatelliteService =
            new AtomicBoolean(false);
    private final AtomicBoolean mRegisteredForSatelliteModemStateChangedWithSatelliteService =
@@ -349,9 +346,11 @@ public class SatelliteController extends Handler {
    private final Object mIsRadioOnLock = new Object();
    @GuardedBy("mIsRadioOnLock")
    private boolean mIsRadioOn = false;
    private final Object mSatelliteViaOemProvisionLock = new Object();
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    protected final Object mSatelliteViaOemProvisionLock = new Object();
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    @GuardedBy("mSatelliteViaOemProvisionLock")
    private Boolean mIsSatelliteViaOemProvisioned = null;
    protected Boolean mIsSatelliteViaOemProvisioned = null;
    @GuardedBy("mSatelliteViaOemProvisionLock")
    private Boolean mOverriddenIsSatelliteViaOemProvisioned = null;
    private final Object mSatelliteCapabilitiesLock = new Object();
@@ -584,7 +583,6 @@ public class SatelliteController extends Handler {
            mIsRadioOn = phone.isRadioOn();
        }

        registerForSatelliteProvisionStateChanged();
        registerForPendingDatagramCount();
        registerForSatelliteModemStateChanged();
        registerForServiceStateChanged();
@@ -1022,11 +1020,12 @@ public class SatelliteController extends Handler {
                    break;
                }
                mSatelliteProvisionCallbacks.put(argument.subId, argument.callback);
                onCompleted = obtainMessage(EVENT_PROVISION_SATELLITE_SERVICE_DONE, request);
                // Log the current time for provision triggered
                mProvisionMetricsStats.setProvisioningStartTime();
                mSatelliteModemInterface.provisionSatelliteService(argument.token,
                        argument.provisionData, onCompleted);
                Message provisionSatelliteServiceDoneEvent = this.obtainMessage(
                        EVENT_PROVISION_SATELLITE_SERVICE_DONE,
                        new AsyncResult(request, SATELLITE_RESULT_SUCCESS, null));
                provisionSatelliteServiceDoneEvent.sendToTarget();
                break;
            }

@@ -1045,11 +1044,13 @@ public class SatelliteController extends Handler {
                request = (SatelliteControllerHandlerRequest) msg.obj;
                ProvisionSatelliteServiceArgument argument =
                        (ProvisionSatelliteServiceArgument) request.argument;
                onCompleted = obtainMessage(EVENT_DEPROVISION_SATELLITE_SERVICE_DONE, request);
                if (argument.callback != null) {
                    mProvisionMetricsStats.setProvisioningStartTime();
                }
                mSatelliteModemInterface.deprovisionSatelliteService(argument.token, onCompleted);
                Message deprovisionSatelliteServiceDoneEvent = this.obtainMessage(
                        EVENT_DEPROVISION_SATELLITE_SERVICE_DONE,
                        new AsyncResult(request, SATELLITE_RESULT_SUCCESS, null));
                deprovisionSatelliteServiceDoneEvent.sendToTarget();
                break;
            }

@@ -1346,8 +1347,10 @@ public class SatelliteController extends Handler {

            case CMD_IS_SATELLITE_PROVISIONED: {
                request = (SatelliteControllerHandlerRequest) msg.obj;
                onCompleted = obtainMessage(EVENT_IS_SATELLITE_PROVISIONED_DONE, request);
                mSatelliteModemInterface.requestIsSatelliteProvisioned(onCompleted);
                Message isProvisionedDoneEvent = this.obtainMessage(
                        EVENT_IS_SATELLITE_PROVISIONED_DONE,
                        new AsyncResult(request, SATELLITE_RESULT_SUCCESS, null));
                isProvisionedDoneEvent.sendToTarget();
                break;
            }

@@ -1356,15 +1359,6 @@ public class SatelliteController extends Handler {
                break;
            }

            case EVENT_SATELLITE_PROVISION_STATE_CHANGED:
                ar = (AsyncResult) msg.obj;
                if (ar.result == null) {
                    ploge("EVENT_SATELLITE_PROVISION_STATE_CHANGED: result is null");
                } else {
                    handleEventSatelliteProvisionStateChanged((boolean) ar.result);
                }
                break;

            case EVENT_PENDING_DATAGRAMS:
                plogd("Received EVENT_PENDING_DATAGRAMS");
                IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
@@ -2051,12 +2045,7 @@ public class SatelliteController extends Handler {
            return;
        }

        Boolean satelliteProvisioned = isSatelliteViaOemProvisioned();
        if (satelliteProvisioned == null) {
            result.accept(SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE);
            return;
        }
        if (!satelliteProvisioned) {
        if (Boolean.FALSE.equals(isSatelliteViaOemProvisioned())) {
            result.accept(SATELLITE_RESULT_SUCCESS);
            return;
        }
@@ -2083,6 +2072,18 @@ public class SatelliteController extends Handler {
        }

        mSatelliteProvisionStateChangedListeners.put(callback.asBinder(), callback);

        boolean isProvisioned = Boolean.TRUE.equals(isSatelliteViaOemProvisioned());
        try {
            callback.onSatelliteProvisionStateChanged(isProvisioned);
        } catch (RemoteException ex) {
            loge("setSatelliteServicePackageName: " + ex);
        }
        synchronized (mSatelliteViaOemProvisionLock) {
            plogd("registerForSatelliteProvisionStateChanged: report current provisioned "
                    + "state, state=" + isProvisioned);
        }

        return SATELLITE_RESULT_SUCCESS;
    }

@@ -2584,26 +2585,28 @@ public class SatelliteController extends Handler {
     * This API can be used by only CTS to update satellite vendor service package name.
     *
     * @param servicePackageName The package name of the satellite vendor service.
     * @param provisioned          Whether satellite should be provisioned or not.
     * @return {@code true} if the satellite vendor service is set successfully,
     * {@code false} otherwise.
     */
    public boolean setSatelliteServicePackageName(@Nullable String servicePackageName) {
        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
            plogd("setSatelliteServicePackageName: oemEnabledSatelliteFlag is disabled");
            return false;
        }
    public boolean setSatelliteServicePackageName(@Nullable String servicePackageName,
            String provisioned) {
        if (!isMockModemAllowed()) {
            plogd("setSatelliteServicePackageName: mock modem not allowed");
            return false;
        }

        // Cached states need to be cleared whenever switching satellite vendor services.
        plogd("setSatelliteServicePackageName: Resetting cached states");
        plogd("setSatelliteServicePackageName: Resetting cached states, provisioned="
                + provisioned);
        synchronized (mIsSatelliteSupportedLock) {
            mIsSatelliteSupported = null;
        }
        synchronized (mSatelliteViaOemProvisionLock) {
            mIsSatelliteViaOemProvisioned = null;
            mIsSatelliteViaOemProvisioned = Optional.ofNullable(provisioned)
                    .filter(s -> s.equalsIgnoreCase("true") || s.equalsIgnoreCase("false"))
                    .map(s -> s.equalsIgnoreCase("true"))
                    .orElse(null);
        }
        synchronized (mIsSatelliteEnabledLock) {
            mIsSatelliteEnabled = null;
@@ -3483,38 +3486,6 @@ public class SatelliteController extends Handler {
        msg.sendToTarget();
    }

    /**
     * Posts the specified command to be executed on the main thread. As this is a synchronous
     * request, it waits until the request is complete and then return the result.
     *
     * @param command command to be executed on the main thread
     * @param argument additional parameters required to perform of the operation
     * @param phone phone object used to perform the operation.
     * @return result of the operation
     */
    private @Nullable Object sendRequest(int command, @NonNull Object argument,
            @Nullable Phone phone) {
        if (Looper.myLooper() == this.getLooper()) {
            throw new RuntimeException("This method will deadlock if called from the main thread");
        }

        SatelliteControllerHandlerRequest request = new SatelliteControllerHandlerRequest(
                argument, phone);
        Message msg = this.obtainMessage(command, request);
        msg.sendToTarget();

        synchronized (request) {
            while(request.result == null) {
                try {
                    request.wait();
                } catch (InterruptedException e) {
                    // Do nothing, go back and wait until the request is complete.
                }
            }
        }
        return request.result;
    }

    /**
     * Check if satellite is provisioned for a subscription on the device.
     * @return true if satellite is provisioned on the given subscription else return false.
@@ -3530,16 +3501,13 @@ public class SatelliteController extends Handler {
            if (mIsSatelliteViaOemProvisioned != null) {
                return mIsSatelliteViaOemProvisioned;
            }

            if (mIsSatelliteViaOemProvisioned == null) {
                mIsSatelliteViaOemProvisioned = getPersistedOemEnabledSatelliteProvisionStatus();
            }

        requestIsSatelliteProvisioned(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
                new ResultReceiver(this) {
                    @Override
                    protected void onReceiveResult(int resultCode, Bundle resultData) {
                        plogd("isSatelliteViaOemProvisioned: resultCode=" + resultCode);
            return mIsSatelliteViaOemProvisioned;
        }
                });
        return null;
    }

    private void handleSatelliteEnabled(SatelliteControllerHandlerRequest request) {
@@ -3595,7 +3563,6 @@ public class SatelliteController extends Handler {
                + "changed to " + supported);

        if (supported) {
            registerForSatelliteProvisionStateChanged();
            registerForPendingDatagramCount();
            registerForSatelliteModemStateChanged();
            registerForNtnSignalStrengthChanged();
@@ -3644,16 +3611,6 @@ public class SatelliteController extends Handler {
        }
    }

    private void registerForSatelliteProvisionStateChanged() {
        if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
            if (!mRegisteredForProvisionStateChangedWithSatelliteService.get()) {
                mSatelliteModemInterface.registerForSatelliteProvisionStateChanged(
                        this, EVENT_SATELLITE_PROVISION_STATE_CHANGED, null);
                mRegisteredForProvisionStateChangedWithSatelliteService.set(true);
            }
        }
    }

    private void registerForPendingDatagramCount() {
        if (mSatelliteModemInterface.isSatelliteServiceSupported()) {
            if (!mRegisteredForPendingDatagramCountWithSatelliteService.get()) {
@@ -4627,6 +4584,7 @@ public class SatelliteController extends Handler {
                return SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
            }
            if (!satelliteProvisioned) {
                plogd("evaluateOemSatelliteRequestAllowed: satellite service is not provisioned");
                return SatelliteManager.SATELLITE_RESULT_SERVICE_NOT_PROVISIONED;
            }
        }
@@ -4882,7 +4840,21 @@ public class SatelliteController extends Handler {
    private void persistOemEnabledSatelliteProvisionStatus(boolean isProvisioned) {
        synchronized (mSatelliteViaOemProvisionLock) {
            plogd("persistOemEnabledSatelliteProvisionStatus: isProvisioned=" + isProvisioned);

            if (mFeatureFlags.carrierRoamingNbIotNtn()) {
                int subId = SatelliteServiceUtils.getOemBasedNonTerrestrialSubscriptionId(mContext);
                if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
                    try {
                        mSubscriptionManagerService.setIsSatelliteProvisionedForNonIpDatagram(subId,
                                isProvisioned);
                        plogd("persistOemEnabledSatelliteProvisionStatus: subId=" + subId);
                    } catch (IllegalArgumentException | SecurityException ex) {
                        ploge("setIsSatelliteProvisionedForNonIpDatagram: subId=" + subId + ", ex="
                                + ex);
                    }
                } else {
                    plogd("persistOemEnabledSatelliteProvisionStatus: INVALID_SUBSCRIPTION_ID");
                }
            } else {
                if (!loadSatelliteSharedPreferences()) return;

                if (mSharedPreferences == null) {
@@ -4893,13 +4865,28 @@ public class SatelliteController extends Handler {
                }
            }
        }
    }

    private boolean getPersistedOemEnabledSatelliteProvisionStatus() {
    @Nullable
    private Boolean getPersistedOemEnabledSatelliteProvisionStatus() {
        plogd("getPersistedOemEnabledSatelliteProvisionStatus:");
        synchronized (mSatelliteViaOemProvisionLock) {
            if (mFeatureFlags.carrierRoamingNbIotNtn()) {
                int subId = SatelliteServiceUtils.getOemBasedNonTerrestrialSubscriptionId(mContext);
                if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
                    return mSubscriptionManagerService.isSatelliteProvisionedForNonIpDatagram(
                            subId);
                } else {
                    plogd("getPersistedOemEnabledSatelliteProvisionStatus: "
                            + "subId=INVALID_SUBSCRIPTION_ID, return null");
                    return null;
                }
            } else {
                if (!loadSatelliteSharedPreferences()) return false;

                if (mSharedPreferences == null) {
                ploge("getPersistedOemEnabledSatelliteProvisionStatus: mSharedPreferences is null");
                    ploge("getPersistedOemEnabledSatelliteProvisionStatus: mSharedPreferences is "
                            + "null");
                    return false;
                } else {
                    return mSharedPreferences.getBoolean(
@@ -4907,6 +4894,7 @@ public class SatelliteController extends Handler {
                }
            }
        }
    }

    private boolean loadSatelliteSharedPreferences() {
        if (mSharedPreferences == null) {
@@ -4924,31 +4912,13 @@ public class SatelliteController extends Handler {
    }

    private void handleIsSatelliteProvisionedDoneEvent(@NonNull AsyncResult ar) {
        logd("handleIsSatelliteProvisionedDoneEvent:");
        SatelliteControllerHandlerRequest request = (SatelliteControllerHandlerRequest) ar.userObj;
        int error = SatelliteServiceUtils.getSatelliteError(
                ar, "handleIsSatelliteProvisionedDoneEvent");
        boolean isSatelliteProvisionedInModem = false;
        if (error == SATELLITE_RESULT_SUCCESS) {
            if (ar.result == null) {
                ploge("handleIsSatelliteProvisionedDoneEvent: result is null");
                error = SatelliteManager.SATELLITE_RESULT_INVALID_TELEPHONY_STATE;
            } else {
                isSatelliteProvisionedInModem = ((int[]) ar.result)[0] == 1;
            }
        } else if (error == SATELLITE_RESULT_REQUEST_NOT_SUPPORTED) {
            plogd("handleIsSatelliteProvisionedDoneEvent: Modem does not support this request");
            isSatelliteProvisionedInModem = true;
        }
        boolean isSatelliteViaOemProvisioned =
                isSatelliteProvisionedInModem && getPersistedOemEnabledSatelliteProvisionStatus();
        plogd("isSatelliteProvisionedInModem=" + isSatelliteProvisionedInModem
                + ", isSatelliteViaOemProvisioned=" + isSatelliteViaOemProvisioned);

        Bundle bundle = new Bundle();
        bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED, isSatelliteViaOemProvisioned);
        synchronized (mSatelliteViaOemProvisionLock) {
            mIsSatelliteViaOemProvisioned = isSatelliteViaOemProvisioned;
        }
        ((ResultReceiver) request.argument).send(error, bundle);
        bundle.putBoolean(SatelliteManager.KEY_SATELLITE_PROVISIONED,
                Boolean.TRUE.equals(isSatelliteViaOemProvisioned()));
        ((ResultReceiver) request.argument).send(SATELLITE_RESULT_SUCCESS, bundle);
    }

    private long getWaitForSatelliteEnablingResponseTimeoutMillis() {
+0 −135
Original line number Diff line number Diff line
@@ -86,8 +86,6 @@ public class SatelliteModemInterface {
    private boolean mIsBinding;
    @Nullable private PersistentLogger mPersistentLogger = null;

    @NonNull private final RegistrantList mSatelliteProvisionStateChangedRegistrants =
            new RegistrantList();
    @NonNull private final RegistrantList mSatellitePositionInfoChangedRegistrants =
            new RegistrantList();
    @NonNull private final RegistrantList mDatagramTransferStateChangedRegistrants =
@@ -112,11 +110,6 @@ public class SatelliteModemInterface {
            mIsDemoListener = isDemoListener;
        }

        @Override
        public void onSatelliteProvisionStateChanged(boolean provisioned) {
            mSatelliteProvisionStateChangedRegistrants.notifyResult(provisioned);
        }

        @Override
        public void onSatelliteDatagramReceived(
                android.telephony.satellite.stub.SatelliteDatagram datagram, int pendingCount) {
@@ -402,27 +395,6 @@ public class SatelliteModemInterface {
        }
    }

    /**
     * Registers for the satellite provision state changed.
     *
     * @param h Handler for notification message.
     * @param what User-defined message code.
     * @param obj User object.
     */
    public void registerForSatelliteProvisionStateChanged(
            @NonNull Handler h, int what, @Nullable Object obj) {
        mSatelliteProvisionStateChangedRegistrants.add(h, what, obj);
    }

    /**
     * Unregisters for the satellite provision state changed.
     *
     * @param h Handler to be removed from the registrant list.
     */
    public void unregisterForSatelliteProvisionStateChanged(@NonNull Handler h) {
        mSatelliteProvisionStateChangedRegistrants.remove(h);
    }

    /**
     * Registers for satellite position info changed from satellite modem.
     *
@@ -888,113 +860,6 @@ public class SatelliteModemInterface {
        }
    }

    /**
     * Provision the device with a satellite provider.
     * This is needed if the provider allows dynamic registration.
     * Once provisioned, ISatelliteListener#onSatelliteProvisionStateChanged should report true.
     *
     * @param token The token to be used as a unique identifier for provisioning with satellite
     *              gateway.
     * @param provisionData Data from the provisioning app that can be used by provisioning server
     * @param message The Message to send to result of the operation to.
     */
    public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData,
            @NonNull Message message) {
        if (mSatelliteService != null) {
            try {
                mSatelliteService.provisionSatelliteService(token, provisionData,
                        new IIntegerConsumer.Stub() {
                            @Override
                            public void accept(int result) {
                                int error = SatelliteServiceUtils.fromSatelliteError(result);
                                plogd("provisionSatelliteService: " + error);
                                Binder.withCleanCallingIdentity(() ->
                                        sendMessageWithResult(message, null, error));
                            }
                        });
            } catch (RemoteException e) {
                ploge("provisionSatelliteService: RemoteException " + e);
                sendMessageWithResult(message, null,
                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
            }
        } else {
            ploge("provisionSatelliteService: Satellite service is unavailable.");
            sendMessageWithResult(message, null,
                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
        }
    }

    /**
     * Deprovision the device with the satellite provider.
     * This is needed if the provider allows dynamic registration.
     * Once deprovisioned, ISatelliteListener#onSatelliteProvisionStateChanged should report false.
     *
     * @param token The token of the device/subscription to be deprovisioned.
     * @param message The Message to send to result of the operation to.
     */
    public void deprovisionSatelliteService(@NonNull String token, @NonNull Message message) {
        if (mSatelliteService != null) {
            try {
                mSatelliteService.deprovisionSatelliteService(token, new IIntegerConsumer.Stub() {
                    @Override
                    public void accept(int result) {
                        int error = SatelliteServiceUtils.fromSatelliteError(result);
                        plogd("deprovisionSatelliteService: " + error);
                        Binder.withCleanCallingIdentity(() ->
                                sendMessageWithResult(message, null, error));
                    }
                });
            } catch (RemoteException e) {
                ploge("deprovisionSatelliteService: RemoteException " + e);
                sendMessageWithResult(message, null,
                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
            }
        } else {
            ploge("deprovisionSatelliteService: Satellite service is unavailable.");
            sendMessageWithResult(message, null,
                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
        }
    }

    /**
     * Request to get whether this device is provisioned with a satellite provider.
     *
     * @param message The Message to send to result of the operation to.
     */
    public void requestIsSatelliteProvisioned(@NonNull Message message) {
        if (mSatelliteService != null) {
            try {
                mSatelliteService.requestIsSatelliteProvisioned(new IIntegerConsumer.Stub() {
                    @Override
                    public void accept(int result) {
                        int error = SatelliteServiceUtils.fromSatelliteError(result);
                        plogd("requestIsSatelliteProvisioned: " + error);
                        Binder.withCleanCallingIdentity(() ->
                                sendMessageWithResult(message, null, error));
                    }
                }, new IBooleanConsumer.Stub() {
                    @Override
                    public void accept(boolean result) {
                        // Convert for compatibility with SatelliteResponse
                        // TODO: This should just report result instead.
                        int[] provisioned = new int[] {result ? 1 : 0};
                        plogd("requestIsSatelliteProvisioned: " + Arrays.toString(provisioned));
                        Binder.withCleanCallingIdentity(() -> sendMessageWithResult(
                                message, provisioned, SatelliteManager.SATELLITE_RESULT_SUCCESS));
                    }
                });
            } catch (RemoteException e) {
                ploge("requestIsSatelliteProvisioned: RemoteException " + e);
                sendMessageWithResult(message, null,
                        SatelliteManager.SATELLITE_RESULT_SERVICE_ERROR);
            }
        } else {
            ploge("requestIsSatelliteProvisioned: Satellite service is unavailable.");
            sendMessageWithResult(message, null,
                    SatelliteManager.SATELLITE_RESULT_RADIO_NOT_AVAILABLE);
        }
    }

    /**
     * Poll the pending datagrams to be received over satellite.
     * The satellite service should check if there are any pending datagrams to be received over
+21 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.telephony.CellIdentity;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.Rlog;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.satellite.AntennaPosition;
import android.telephony.satellite.NtnSignalStrength;
@@ -304,6 +305,26 @@ public class SatelliteServiceUtils {
        return SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
    }

    /**
     * Get the subscription ID which supports OEM based NTN satellite service.
     *
     * @return ID of the subscription that supports OEM-based satellite if any,
     * return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} otherwise.
     */
    public static int getOemBasedNonTerrestrialSubscriptionId(@NonNull Context context) {
        List<SubscriptionInfo> infoList =
                SubscriptionManagerService.getInstance().getAllSubInfoList(
                        context.getOpPackageName(), null);

        int subId = infoList.stream()
                .filter(info -> info.isOnlyNonTerrestrialNetwork())
                .mapToInt(SubscriptionInfo::getSubscriptionId)
                .findFirst()
                .orElse(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
        logd("getOemBasedNonTerrestrialSubscriptionId: subId=" + subId);
        return subId;
    }

    /**
     * Expected format of the input dictionary bundle is:
     * <ul>
Loading