Merge "Correct edge to edge issues observed in some screens." into main
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 4a9a37d..5f3fc97 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -823,7 +823,12 @@
             configToSend.putAll(config);
         }
 
-        SubscriptionManagerService.getInstance().updateSubscriptionByCarrierConfig(
+        SubscriptionManagerService sm = SubscriptionManagerService.getInstance();
+        if (sm == null) {
+            loge("SubscriptionManagerService missing");
+            return;
+        }
+        sm.updateSubscriptionByCarrierConfig(
                 phoneId, configPackageName, configToSend,
                 () -> mHandler.obtainMessage(EVENT_SUBSCRIPTION_INFO_UPDATED, phoneId, -1)
                         .sendToTarget());
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 0a7fc3e..a3d8baf 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -14478,11 +14478,6 @@
      * @return {@code true} if the operation is successful, {@code false} otherwise.
      */
     public boolean setShouldSendDatagramToModemInDemoMode(boolean shouldSendToModemInDemoMode) {
-        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            Log.d(LOG_TAG, "shouldSendDatagramToModemInDemoMode: oemEnabledSatelliteFlag is "
-                    + "disabled");
-            return false;
-        }
         Log.d(LOG_TAG, "setShouldSendDatagramToModemInDemoMode");
         TelephonyPermissions.enforceShellOnly(
                 Binder.getCallingUid(), "setShouldSendDatagramToModemInDemoMode");
@@ -14506,12 +14501,6 @@
      * @return {@code true} if the setting is successful, {@code false} otherwise.
      */
     public boolean setIsSatelliteCommunicationAllowedForCurrentLocationCache(String state) {
-        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            Log.d(LOG_TAG, "setIsSatelliteCommunicationAllowedForCurrentLocationCache: "
-                    + "oemEnabledSatelliteFlag is disabled");
-            return false;
-        }
-
         Log.d(LOG_TAG, "setIsSatelliteCommunicationAllowedForCurrentLocationCache: "
                 + "state=" + state);
         TelephonyPermissions.enforceShellOnly(
diff --git a/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java b/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
index 67d983f..e2a94bf 100644
--- a/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
+++ b/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
@@ -730,11 +730,6 @@
      */
     public void requestIsCommunicationAllowedForCurrentLocation(
             @NonNull ResultReceiver result, boolean enablingSatellite) {
-        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            plogd("oemEnabledSatelliteFlag is disabled");
-            result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null);
-            return;
-        }
         plogd("requestIsCommunicationAllowedForCurrentLocation : "
                 + "enablingSatellite is " + enablingSatellite);
         synchronized (mIsAllowedCheckBeforeEnablingSatelliteLock) {
@@ -1520,11 +1515,6 @@
     }
 
     private void registerLocationModeChangedBroadcastReceiver(Context context) {
-        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            plogd("registerLocationModeChangedBroadcastReceiver: Flag "
-                    + "oemEnabledSatellite is disabled");
-            return;
-        }
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION);
         intentFilter.addAction(LocationManager.PROVIDERS_CHANGED_ACTION);
@@ -1979,11 +1969,6 @@
     }
 
     private void handleSatelliteAllowedRegionPossiblyChanged(int handleEvent) {
-        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            ploge("handleSatelliteAllowedRegionPossiblyChanged: "
-                    + "The feature flag oemEnabledSatelliteFlag() is not enabled");
-            return;
-        }
         synchronized (mPossibleChangeInSatelliteAllowedRegionLock) {
             logd("handleSatelliteAllowedRegionPossiblyChanged");
             setIsSatelliteAllowedRegionPossiblyChanged(true);
@@ -2878,12 +2863,6 @@
     @SatelliteManager.SatelliteResult
     public int registerForCommunicationAccessStateChanged(int subId,
             @NonNull ISatelliteCommunicationAccessStateCallback callback) {
-        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            plogd("registerForCommunicationAccessStateChanged: oemEnabledSatelliteFlag is "
-                    + "disabled");
-            return SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
-        }
-
         mSatelliteCommunicationAccessStateChangedListeners.put(callback.asBinder(), callback);
 
         this.post(() -> {
@@ -2923,12 +2902,6 @@
      */
     public void unregisterForCommunicationAccessStateChanged(
             int subId, @NonNull ISatelliteCommunicationAccessStateCallback callback) {
-        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            plogd("unregisterForCommunicationAccessStateChanged: "
-                    + "oemEnabledSatelliteFlag is disabled");
-            return;
-        }
-
         mSatelliteCommunicationAccessStateChangedListeners.remove(callback.asBinder());
     }
 
@@ -3007,12 +2980,6 @@
      * @return {@code true} if the setting is successful, {@code false} otherwise.
      */
     public boolean setIsSatelliteCommunicationAllowedForCurrentLocationCache(String state) {
-        if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
-            logd("setIsSatelliteCommunicationAllowedForCurrentLocationCache: "
-                    + "oemEnabledSatelliteFlag is disabled");
-            return false;
-        }
-
         if (!isMockModemAllowed()) {
             logd("setIsSatelliteCommunicationAllowedForCurrentLocationCache: "
                     + "mock modem not allowed.");
diff --git a/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponse.java b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponse.java
index 7d6b5ba..a9e2f39 100644
--- a/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponse.java
+++ b/src/com/android/phone/satellite/entitlement/SatelliteEntitlementResponse.java
@@ -21,7 +21,6 @@
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.satellite.SatelliteNetworkInfo;
 import com.android.libraries.entitlement.ServiceEntitlement;
 
@@ -135,7 +134,7 @@
                 for (int i = 0; i < jsonArray.length(); i++) {
                     String dataPlanType = jsonArray.getJSONObject(i).has(DATA_PLAN_TYPE_KEY)
                             ? jsonArray.getJSONObject(i).getString(DATA_PLAN_TYPE_KEY) : "";
-                    Map<String, String> allowedServicesInfo = new HashMap<>();
+                    Map<String, String> allowedServicesInfo = null;
                     if (jsonArray.getJSONObject(i).has(ALLOWED_SERVICES_INFO_TYPE_KEY)) {
                         allowedServicesInfo = new HashMap<>();
                         JSONArray jsonArray1 = jsonArray.getJSONObject(i)
diff --git a/src/com/android/phone/security/SafetySourceReceiver.java b/src/com/android/phone/security/SafetySourceReceiver.java
index 34edb73..99394c2 100644
--- a/src/com/android/phone/security/SafetySourceReceiver.java
+++ b/src/com/android/phone/security/SafetySourceReceiver.java
@@ -34,13 +34,6 @@
     private static final String TAG = "TelephonySafetySourceReceiver";
     @Override
     public void onReceive(Context context, Intent intent) {
-
-        // If none of the features that depend on this receiver are enabled, there's no reason
-        // to progress.
-        if (!Flags.enableModemCipherTransparencyUnsolEvents()) {
-            return;
-        }
-
         String action = intent.getAction();
         if (!ACTION_REFRESH_SAFETY_SOURCES.equals(action)) {
             return;
diff --git a/src/com/android/phone/settings/RadioInfo.java b/src/com/android/phone/settings/RadioInfo.java
index 42c6f2e..ab7a3bf 100644
--- a/src/com/android/phone/settings/RadioInfo.java
+++ b/src/com/android/phone/settings/RadioInfo.java
@@ -944,6 +944,7 @@
         mSimulateOutOfServiceSwitch.setOnCheckedChangeListener(mSimulateOosOnChangeListener);
         mMockSatellite.setChecked(mCarrierSatelliteOriginalBundle[mPhoneId] != null);
         mMockSatellite.setOnCheckedChangeListener(mMockSatelliteListener);
+        mMockSatelliteDataSwitch.setChecked(mSatelliteDataOriginalBundle[mPhoneId] != null);
         mMockSatelliteDataSwitch.setOnCheckedChangeListener(mMockSatelliteDataSwitchListener);
         mMockSatelliteData.setOnCheckedChangeListener(mMockSatelliteDataListener);
 
@@ -2237,19 +2238,24 @@
                 overrideBundle.putBoolean(
                         KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);
                 log("satData: mMockSatelliteDataListener: new " + overrideBundle);
-                getCarrierConfig().overrideConfig(mSubId, overrideBundle, false);
+                if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
+                    getCarrierConfig().overrideConfig(mSubId, overrideBundle, false);
+                } else {
+                    Log.e(TAG, "SubscriptionId is not valid: " + mSubId);
+                }
             };
 
     private final OnCheckedChangeListener mMockSatelliteDataSwitchListener =
             (buttonView, isChecked) -> {
         log("satData: ServiceData enabling = " + isChecked);
         if (isChecked) {
-            if (!isValidOperator()) {
+            if (isValidOperator(mSubId)) {
+                updateSatelliteDataButton();
+            } else {
                 log("satData: Not a valid Operator");
                 mMockSatelliteDataSwitch.setChecked(false);
                 return;
             }
-            updateSatelliteDataButton();
         } else {
             reloadCarrierConfigDefaults();
         }
@@ -2273,30 +2279,36 @@
         }
     }
 
-    private boolean isValidOperator() {
-        String operatorNumeric = mTelephonyManager
-                .getNetworkOperatorForPhone(mPhoneId);
-        TelephonyManager tm;
-        if (TextUtils.isEmpty(operatorNumeric)
-                && (tm = getSystemService(TelephonyManager.class)) != null) {
-            operatorNumeric = tm.getSimOperatorNumericForPhone(mPhoneId);
+    private boolean isValidOperator(int subId) {
+        String operatorNumeric = null;
+        if (SubscriptionManager.isValidSubscriptionId(subId)) {
+            operatorNumeric = mTelephonyManager
+                    .getNetworkOperatorForPhone(mPhoneId);
+            TelephonyManager tm;
+            if (TextUtils.isEmpty(operatorNumeric)
+                    && (tm = getSystemService(TelephonyManager.class)) != null) {
+                operatorNumeric = tm.getSimOperatorNumericForPhone(mPhoneId);
+            }
         }
         return !TextUtils.isEmpty(operatorNumeric);
     }
 
     private final OnCheckedChangeListener mMockSatelliteListener =
             (buttonView, isChecked) -> {
-                if (SubscriptionManager.isValidPhoneId(mPhoneId)) {
+                int subId = mSubId;
+                int phoneId = mPhoneId;
+                if (SubscriptionManager.isValidPhoneId(phoneId)
+                        && SubscriptionManager.isValidSubscriptionId(subId)) {
                     if (getCarrierConfig() == null) return;
                     if (isChecked) {
-                        if (!isValidOperator()) {
+                        if (!isValidOperator(subId)) {
                             mMockSatellite.setChecked(false);
                             loge("mMockSatelliteListener: Can't mock because no operator for phone "
-                                    + mPhoneId);
+                                    + phoneId);
                             return;
                         }
                         PersistableBundle originalBundle = getCarrierConfig().getConfigForSubId(
-                                mSubId,
+                                subId,
                                 KEY_SATELLITE_ATTACH_SUPPORTED_BOOL,
                                 KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL,
                                 KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE
@@ -2307,7 +2319,7 @@
                         overrideBundle.putBoolean(KEY_SATELLITE_ENTITLEMENT_SUPPORTED_BOOL, false);
                         PersistableBundle capableProviderBundle = new PersistableBundle();
                         capableProviderBundle.putIntArray(mTelephonyManager
-                                        .getNetworkOperatorForPhone(mPhoneId),
+                                        .getNetworkOperatorForPhone(phoneId),
                                 new int[]{
                                         // Currently satellite only supports below
                                         NetworkRegistrationInfo.SERVICE_TYPE_SMS,
@@ -2318,18 +2330,18 @@
                                 capableProviderBundle);
                         log("mMockSatelliteListener: new " + overrideBundle);
                         log("mMockSatelliteListener: old " + originalBundle);
-                        getCarrierConfig().overrideConfig(mSubId, overrideBundle, false);
-                        mCarrierSatelliteOriginalBundle[mPhoneId] = originalBundle;
+                        getCarrierConfig().overrideConfig(subId, overrideBundle, false);
+                        mCarrierSatelliteOriginalBundle[phoneId] = originalBundle;
                     } else {
                         try {
-                            getCarrierConfig().overrideConfig(mSubId,
-                                    mCarrierSatelliteOriginalBundle[mPhoneId], false);
-                            mCarrierSatelliteOriginalBundle[mPhoneId] = null;
+                            getCarrierConfig().overrideConfig(subId,
+                                    mCarrierSatelliteOriginalBundle[phoneId], false);
+                            mCarrierSatelliteOriginalBundle[phoneId] = null;
                             log("mMockSatelliteListener: Successfully cleared mock for phone "
-                                    + mPhoneId);
+                                    + phoneId);
                         } catch (Exception e) {
                             loge("mMockSatelliteListener: Can't clear mock because invalid sub Id "
-                                    + mSubId
+                                    + subId
                                     + ", insert SIM and use adb shell cmd phone cc clear-values");
                             // Keep show toggle ON if the view is not destroyed. If destroyed, must
                             // use cmd to reset, because upon creation the view doesn't remember the
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 8956266..de4076d 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -1790,7 +1790,7 @@
                         }
 
                         // Skip the sim for satellite as it does not support call for now
-                        if (Flags.oemEnabledSatelliteFlag() && info.isOnlyNonTerrestrialNetwork()) {
+                        if (info.isOnlyNonTerrestrialNetwork()) {
                             Log.d(this, "setupAccounts: skipping satellite sub id "
                                     + subscriptionId);
                             continue;
diff --git a/src/com/android/services/telephony/rcs/SipTransportController.java b/src/com/android/services/telephony/rcs/SipTransportController.java
index 2f090d5..efd3e83 100644
--- a/src/com/android/services/telephony/rcs/SipTransportController.java
+++ b/src/com/android/services/telephony/rcs/SipTransportController.java
@@ -823,7 +823,7 @@
         mEvaluateCompleteFuture = pendingChange
                 .whenComplete((f, ex) -> {
                     if (ex != null) {
-                        logw("reevaluateDelegates: Exception caught: " + ex);
+                        logw("reevaluateDelegates: Exception caught", ex);
                     }
                 }).thenAccept((associatedFeatures) -> {
                     logi("reevaluateDelegates: reevaluate complete, feature tags associated: "
@@ -977,8 +977,9 @@
      */
     private Set<FeatureTagState> updateSupportedTags(Set<String> candidateFeatureTags,
             Set<String> previouslyGrantedTags) {
-        Boolean overrideRes = RcsProvisioningMonitor.getInstance()
-                .getImsFeatureValidationOverride(mSubId);
+        RcsProvisioningMonitor monitor = RcsProvisioningMonitor.getInstance();
+        Boolean overrideRes = monitor == null ? null :
+                monitor.getImsFeatureValidationOverride(mSubId);
         // deny tags already used by other delegates
         Set<FeatureTagState> deniedTags = new ArraySet<>();
 
@@ -1212,4 +1213,9 @@
         Log.w(LOG_TAG, "[" + mSlotId  + "->" + mSubId + "] " + log);
         mLocalLog.log("[W] " + log);
     }
+
+    private void logw(String log, Throwable ex) {
+        Log.w(LOG_TAG, "[" + mSlotId  + "->" + mSubId + "] " + log, ex);
+        mLocalLog.log("[W] " + log + ": " + ex);
+    }
 }
diff --git a/testapps/TestSatelliteApp/AndroidManifest.xml b/testapps/TestSatelliteApp/AndroidManifest.xml
index a1f22fa..1825df2 100644
--- a/testapps/TestSatelliteApp/AndroidManifest.xml
+++ b/testapps/TestSatelliteApp/AndroidManifest.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
   ~ Copyright (C) 2024 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,31 +16,19 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.phone.testapps.satellitetestapp">
-    <uses-permission android:name="android.permission.BIND_SATELLITE_SERVICE"/>
-    <uses-permission android:name="android.permission.SATELLITE_COMMUNICATION"/>
-    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
-    <uses-permission android:name="android.permission.SEND_SMS"/>
+
     <application android:label="SatelliteTestApp">
-        <activity android:name=".SatelliteTestApp"
-             android:label="SatelliteTestApp"
-             android:exported="true">
+        <activity
+            android:name=".SatelliteTestApp"
+            android:exported="true"
+            android:label="SatelliteTestApp">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.DEFAULT"/>
-                <category android:name="android.intent.category.LAUNCHER"/>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-
-        <service android:name=".TestSatelliteService"
-             android:directBootAware="true"
-             android:persistent="true"
-             android:permission="android.permission.BIND_SATELLITE_SERVICE"
-             android:exported="true">
-            <intent-filter>
-                <action android:name="android.telephony.satellite.SatelliteService"/>
-            </intent-filter>
-        </service>
-
         <activity android:name=".SatelliteControl" />
         <activity android:name=".Datagram" />
         <activity android:name=".Provisioning" />
@@ -49,5 +36,29 @@
         <activity android:name=".SendReceive" />
         <activity android:name=".NbIotSatellite" />
         <activity android:name=".TestSatelliteWrapper" />
+
+        <receiver
+            android:name=".SatelliteTestAppReceiver"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.phone.testapps.satellitetestapp.RECEIVER" />
+            </intent-filter>
+        </receiver>
+
+        <service
+            android:name=".TestSatelliteService"
+            android:directBootAware="true"
+            android:exported="true"
+            android:permission="android.permission.BIND_SATELLITE_SERVICE"
+            android:persistent="true">
+            <intent-filter>
+                <action android:name="android.telephony.satellite.SatelliteService" />
+            </intent-filter>
+        </service>
     </application>
+
+    <uses-permission android:name="android.permission.SATELLITE_COMMUNICATION" />
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+    <uses-permission android:name="android.permission.SEND_SMS" />
+    <uses-permission android:name="android.permission.BIND_SATELLITE_SERVICE" />
 </manifest>
diff --git a/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTestAppReceiver.java b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTestAppReceiver.java
new file mode 100644
index 0000000..693ac7a
--- /dev/null
+++ b/testapps/TestSatelliteApp/src/com/android/phone/testapps/satellitetestapp/SatelliteTestAppReceiver.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone.testapps.satellitetestapp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.CancellationSignal;
+import android.telephony.satellite.EnableRequestAttributes;
+import android.telephony.satellite.SatelliteManager;
+import android.telephony.satellite.stub.SatelliteResult;
+import android.util.Log;
+
+import java.util.Objects;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class SatelliteTestAppReceiver extends BroadcastReceiver {
+    private static final String TAG = "SatelliteTestAppRcvr";
+
+    private static final long TEST_REQUEST_TIMEOUT = TimeUnit.SECONDS.toMillis(3);
+    private static final String TEST_SATELLITE_TOKEN = "SATELLITE_TOKEN";
+    private static final String ACTION = "com.android.phone.testapps.satellitetestapp.RECEIVER";
+    private static final String ACTION_PROVISION = "provision";
+    private static final String ACTION_DEPROVISION = "deprovision";
+    private static final String ACTION_ENABLE = "enable";
+    private static final String ACTION_DISABLE = "disable";
+    private static final String PARAM_ACTION_KEY = "action_key";
+    private static final String PARAM_DEMO_MODE = "demo_mode";
+
+    private static SatelliteManager mSatelliteManager;
+
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.d(TAG, "onReceive: intent: " + intent.toString());
+
+        String action = intent.getAction();
+        if (!Objects.equals(action, ACTION)) {
+            Log.d(TAG, "Unsupported action: " + action + ", exiting.");
+            return;
+        }
+
+        String param = intent.getStringExtra(PARAM_ACTION_KEY);
+        if (param == null) {
+            Log.d(TAG, "No param provided, exiting");
+            return;
+        }
+
+        if (mSatelliteManager == null) {
+            mSatelliteManager = context.getSystemService(SatelliteManager.class);
+        }
+
+        if (mSatelliteManager == null) {
+            Log.d(TAG, "Satellite Manager is not available, exiting.");
+            return;
+        }
+
+        switch (param) {
+            case ACTION_PROVISION -> provisionSatellite();
+            case ACTION_DEPROVISION -> deprovisionSatellite();
+            case ACTION_ENABLE -> {
+                boolean demoMode = intent.getBooleanExtra(PARAM_DEMO_MODE, true);
+                enableSatellite(demoMode);
+            }
+            case ACTION_DISABLE -> disableSatellite();
+            default -> Log.d(TAG, "Unsupported param:" + param);
+        }
+    }
+
+    private void provisionSatellite() {
+        CancellationSignal cancellationSignal = new CancellationSignal();
+        LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
+        String mText = "This is test provision data.";
+        byte[] testProvisionData = mText.getBytes();
+        mSatelliteManager.provisionService(TEST_SATELLITE_TOKEN, testProvisionData,
+                cancellationSignal, Runnable::run, error::offer);
+        try {
+            Integer value = error.poll(TEST_REQUEST_TIMEOUT, TimeUnit.MILLISECONDS);
+            if (value == null) {
+                Log.d(TAG, "Timed out to provision the satellite");
+            } else if (value != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
+                Log.d(TAG, "Failed to provision the satellite, error ="
+                        + SatelliteErrorUtils.mapError(value));
+            } else {
+                Log.d(TAG, "Successfully provisioned the satellite");
+            }
+        } catch (InterruptedException e) {
+            Log.d(TAG, "Provision SatelliteService exception caught =" + e);
+        }
+    }
+
+    private void deprovisionSatellite() {
+        LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
+        mSatelliteManager.deprovisionService(TEST_SATELLITE_TOKEN, Runnable::run,
+                error::offer);
+        try {
+            Integer value = error.poll(TEST_REQUEST_TIMEOUT, TimeUnit.MILLISECONDS);
+            if (value == null) {
+                Log.d(TAG, "Timed out to deprovision the satellite");
+            } else if (value != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
+                Log.d(TAG, "Failed to deprovision the satellite, error ="
+                        + SatelliteErrorUtils.mapError(value));
+            } else {
+                Log.d(TAG, "Successfully deprovisioned the satellite");
+            }
+        } catch (InterruptedException e) {
+            Log.d(TAG, "Deprovision SatelliteService exception caught =" + e);
+        }
+    }
+
+    private void enableSatellite(boolean isDemoMode) {
+        LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
+        mSatelliteManager.requestEnabled(
+                new EnableRequestAttributes.Builder(true)
+                        .setDemoMode(isDemoMode)
+                        .setEmergencyMode(true)
+                        .build(), Runnable::run, error::offer);
+        Log.d(TAG, "enableSatelliteApp: isDemoMode=" + isDemoMode);
+        try {
+            Integer value = error.poll(TEST_REQUEST_TIMEOUT, TimeUnit.MILLISECONDS);
+            if (value == null) {
+                Log.d(TAG, "Timed out to enable the satellite");
+            } else if (value != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
+                Log.d(TAG, "Failed to enable the satellite, error ="
+                        + SatelliteErrorUtils.mapError(value));
+            } else {
+                Log.d(TAG, "Successfully enabled the satellite");
+            }
+        } catch (InterruptedException e) {
+            Log.d(TAG, "Enable SatelliteService exception caught =" + e);
+        }
+    }
+
+    private void disableSatellite() {
+        LinkedBlockingQueue<Integer> error = new LinkedBlockingQueue<>(1);
+        mSatelliteManager.requestEnabled(new EnableRequestAttributes.Builder(false).build(),
+                Runnable::run, error::offer);
+        try {
+            Integer value = error.poll(TEST_REQUEST_TIMEOUT, TimeUnit.MILLISECONDS);
+            if (value == null) {
+                Log.d(TAG, "Timed out to enable the satellite");
+            } else if (value != SatelliteResult.SATELLITE_RESULT_SUCCESS) {
+                Log.d(TAG, "Failed to enable the satellite, error ="
+                        + SatelliteErrorUtils.mapError(value));
+            } else {
+                Log.d(TAG, "Successfully disabled the satellite");
+            }
+        } catch (InterruptedException e) {
+            Log.d(TAG, "Disable SatelliteService exception caught =" + e);
+        }
+    }
+}
diff --git a/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java b/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java
index f61455d..d532289 100644
--- a/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java
+++ b/tests/src/com/android/phone/satellite/accesscontrol/SatelliteAccessControllerTest.java
@@ -126,6 +126,7 @@
 import com.android.internal.telephony.satellite.SatelliteController;
 import com.android.internal.telephony.satellite.SatelliteModemInterface;
 import com.android.internal.telephony.satellite.metrics.ControllerMetricsStats;
+import com.android.internal.telephony.subscription.SubscriptionManagerService;
 
 import org.junit.After;
 import org.junit.Before;
@@ -354,6 +355,8 @@
                 mMockSatelliteController);
         replaceInstance(SatelliteModemInterface.class, "sInstance", null,
                 mMockSatelliteModemInterface);
+        replaceInstance(SubscriptionManagerService.class, "sInstance", null,
+                mock(SubscriptionManagerService.class));
         replaceInstance(TelephonyCountryDetector.class, "sInstance", null,
                 mMockCountryDetector);
         replaceInstance(ControllerMetricsStats.class, "sInstance", null,
@@ -415,7 +418,6 @@
 
         when(mMockFeatureFlags.satellitePersistentLogging()).thenReturn(true);
         when(mMockFeatureFlags.geofenceEnhancementForBetterUx()).thenReturn(true);
-        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
         when(mMockFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
 
         when(mMockContext.getSystemService(Context.TELEPHONY_SERVICE))
@@ -507,8 +509,6 @@
 
     @Test
     public void testIsSatelliteAccessAllowedForLocation() {
-        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
-
         // Test disallowList case
         when(mMockResources.getBoolean(
                 com.android.internal.R.bool.config_oem_enabled_satellite_access_allow))
@@ -694,20 +694,9 @@
                 "mSatelliteCommunicationAccessStateChangedListeners", mSatelliteAccessControllerUT,
                 mSatelliteCommunicationAllowedStateCallbackMap);
 
-        doReturn(false).when(mMockFeatureFlags).oemEnabledSatelliteFlag();
         int result = mSatelliteAccessControllerUT.registerForCommunicationAccessStateChanged(
                 DEFAULT_SUBSCRIPTION_ID, mockSatelliteAllowedStateCallback);
         mTestableLooper.processAllMessages();
-        assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, result);
-        verify(mockSatelliteAllowedStateCallback, never())
-                .onAccessAllowedStateChanged(anyBoolean());
-        verify(mockSatelliteAllowedStateCallback, never())
-                .onAccessConfigurationChanged(any(SatelliteAccessConfiguration.class));
-
-        doReturn(true).when(mMockFeatureFlags).oemEnabledSatelliteFlag();
-        result = mSatelliteAccessControllerUT.registerForCommunicationAccessStateChanged(
-                DEFAULT_SUBSCRIPTION_ID, mockSatelliteAllowedStateCallback);
-        mTestableLooper.processAllMessages();
         assertEquals(SATELLITE_RESULT_SUCCESS, result);
         verify(mockSatelliteAllowedStateCallback, times(1))
                 .onAccessAllowedStateChanged(anyBoolean());
@@ -876,8 +865,6 @@
 
     @Test
     public void testIsRegionDisallowed() throws Exception {
-        // setup to make the return value of mQueriedSatelliteAllowed 'true'
-        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
         when(mMockContext.getResources()).thenReturn(mMockResources);
         when(mMockResources.getBoolean(
                 com.android.internal.R.bool.config_oem_enabled_satellite_access_allow))
@@ -1076,18 +1063,6 @@
 
     @Test
     public void testRequestIsSatelliteCommunicationAllowedForCurrentLocation() throws Exception {
-        // OEM-enabled satellite is not supported
-        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(false);
-        mSatelliteAccessControllerUT.requestIsCommunicationAllowedForCurrentLocation(
-                mSatelliteAllowedReceiver, false);
-        mTestableLooper.processAllMessages();
-        assertTrue(waitForRequestIsSatelliteAllowedForCurrentLocationResult(
-                mSatelliteAllowedSemaphore, 1));
-        assertEquals(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, mQueriedSatelliteAllowedResultCode);
-
-        // OEM-enabled satellite is supported
-        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
-
         // Satellite is not supported
         setUpResponseForRequestIsSatelliteSupported(false, SATELLITE_RESULT_SUCCESS);
         clearAllInvocations();
@@ -1241,15 +1216,12 @@
         long lastKnownLocationElapsedRealtime =
                 firstMccChangedTime + TEST_LOCATION_QUERY_THROTTLE_INTERVAL_NANOS;
 
-        // OEM-enabled satellite is supported
-        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
-
         verify(mMockCountryDetector).registerForCountryCodeChanged(
                 mCountryDetectorHandlerCaptor.capture(), mCountryDetectorIntCaptor.capture(),
                 mCountryDetectorObjCaptor.capture());
 
         assertSame(mCountryDetectorHandlerCaptor.getValue(), mSatelliteAccessControllerUT);
-        assertSame(mCountryDetectorIntCaptor.getValue(), EVENT_COUNTRY_CODE_CHANGED);
+        assertSame(EVENT_COUNTRY_CODE_CHANGED, mCountryDetectorIntCaptor.getValue());
         assertNull(mCountryDetectorObjCaptor.getValue());
 
         // Setup to invoke GPS query
@@ -1365,9 +1337,6 @@
 
     @Test
     public void testValidatePossibleChangeInSatelliteAllowedRegion() throws Exception {
-        // OEM-enabled satellite is supported
-        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
-
         verify(mMockCountryDetector).registerForCountryCodeChanged(
                 mCountryDetectorHandlerCaptor.capture(), mCountryDetectorIntCaptor.capture(),
                 mCountryDetectorObjCaptor.capture());
@@ -1419,8 +1388,6 @@
 
     @Test
     public void testRetryValidatePossibleChangeInSatelliteAllowedRegion() throws Exception {
-        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
-
         verify(mMockCountryDetector).registerForCountryCodeChanged(
                 mCountryDetectorHandlerCaptor.capture(), mCountryDetectorIntCaptor.capture(),
                 mCountryDetectorObjCaptor.capture());
@@ -1453,11 +1420,6 @@
 
     @Test
     public void testLoadSatelliteAccessConfigurationFromDeviceConfig() {
-        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(false);
-        assertNull(mSatelliteAccessControllerUT
-                .getSatelliteConfigurationFileNameFromOverlayConfig(mMockContext));
-
-        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
         when(mMockContext.getResources()).thenReturn(mMockResources);
         when(mMockResources
                 .getString(eq(com.android.internal.R.string.satellite_access_config_file)))
@@ -1744,7 +1706,8 @@
         logd("otaSatelliteAccessConfigFileAbsPath: " + otaSatelliteAccessConfigFileAbsPath);
 
         if (onDeviceSats2FileAbsPath.equals(otaSats2FileAbsPath)
-                || onDeviceSatelliteAccessConfigFileAbsPath.equals(otaSatelliteAccessConfigFile)) {
+                || onDeviceSatelliteAccessConfigFileAbsPath.equals(
+                        otaSatelliteAccessConfigFileAbsPath)) {
             return false;
         }
 
@@ -1973,7 +1936,6 @@
     @Test
     public void testLocationModeChanged() throws Exception {
         // setup for querying GPS not to reset mIsSatelliteAllowedRegionPossiblyChanged false.
-        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
         when(mMockContext.getResources()).thenReturn(mMockResources);
         when(mMockResources.getBoolean(
                 com.android.internal.R.bool.config_oem_enabled_satellite_access_allow))
@@ -2137,7 +2099,6 @@
     @Test
     public void testRequestIsCommunicationAllowedForCurrentLocationWithEnablingSatellite() {
         // Set non-emergency case
-        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
         setUpResponseForRequestIsSatelliteProvisioned(true, SATELLITE_RESULT_SUCCESS);
         when(mMockCountryDetector.getCurrentNetworkCountryIso()).thenReturn(EMPTY_STRING_LIST);
@@ -2174,7 +2135,6 @@
     @Test
     public void testUpdateSystemSelectionChannels() {
         // Set non-emergency case
-        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
         when(mMockFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
 
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
@@ -2334,7 +2294,6 @@
     @Test
     public void testUpdateSystemSelectionChannels_HandleInvalidInput() {
         // Set non-emergency case
-        when(mMockFeatureFlags.oemEnabledSatelliteFlag()).thenReturn(true);
         when(mMockFeatureFlags.carrierRoamingNbIotNtn()).thenReturn(true);
 
         setUpResponseForRequestIsSatelliteSupported(true, SATELLITE_RESULT_SUCCESS);
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 3497167..2fc2757 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -253,6 +253,7 @@
     @Mock EmergencyCallDomainSelectionConnection mEmergencyCallDomainSelectionConnection;
     @Mock NormalCallDomainSelectionConnection mNormalCallDomainSelectionConnection;
     @Mock ImsPhone mImsPhone;
+    @Mock SubscriptionManagerService mSubscriptionManagerService;
     @Mock private SatelliteSOSMessageRecommender mSatelliteSOSMessageRecommender;
     @Mock private EmergencyStateTracker mEmergencyStateTracker;
     @Mock private Resources mMockResources;
@@ -305,8 +306,8 @@
                 (int) invocation.getArgument(2)))
                 .when(mDisconnectCauseFactory).toTelecomDisconnectCause(anyInt(), any(), anyInt());
         mTestConnectionService.setDisconnectCauseFactory(mDisconnectCauseFactory);
-        mTestConnectionService.onCreate();
-        mTestConnectionService.setTelephonyManagerProxy(mTelephonyManagerProxy);
+        replaceInstance(DomainSelectionResolver.class, "sInstance", null,
+                mDomainSelectionResolver);
         replaceInstance(TelephonyConnectionService.class, "mDomainSelectionResolver",
                 mTestConnectionService, mDomainSelectionResolver);
         replaceInstance(TelephonyConnectionService.class, "mEmergencyStateTracker",
@@ -325,9 +326,15 @@
         doReturn(false).when(mDomainSelectionResolver).isDomainSelectionSupported();
         doReturn(null).when(mDomainSelectionResolver).getDomainSelectionConnection(
                 any(), anyInt(), anyBoolean());
+        replaceInstance(SatelliteController.class, "sInstance", null, mSatelliteController);
         replaceInstance(TelephonyConnectionService.class,
                 "mSatelliteController", mTestConnectionService, mSatelliteController);
         doReturn(mMockResources).when(mContext).getResources();
+        replaceInstance(SubscriptionManagerService.class, "sInstance", null,
+                mSubscriptionManagerService);
+
+        mTestConnectionService.onCreate();
+        mTestConnectionService.setTelephonyManagerProxy(mTelephonyManagerProxy);
 
         mBinderStub = (IConnectionService.Stub) mTestConnectionService.onBind(null);
         mSetFlagsRule.disableFlags(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG);
@@ -1510,10 +1517,8 @@
         // Satellite is for emergency
         doReturn(true).when(mSatelliteController).getRequestIsEmergency();
         doReturn(1).when(mSatelliteController).getSelectedSatelliteSubId();
-        SubscriptionManagerService isub = mock(SubscriptionManagerService.class);
-        replaceInstance(SubscriptionManagerService.class, "sInstance", null, isub);
         SubscriptionInfoInternal info = mock(SubscriptionInfoInternal.class);
-        doReturn(info).when(isub).getSubscriptionInfoInternal(1);
+        doReturn(info).when(mSubscriptionManagerService).getSubscriptionInfoInternal(1);
 
         // Setup outgoing emergency call
         setupConnectionServiceInApm();
@@ -1547,10 +1552,8 @@
         // Satellite is for emergency
         doReturn(true).when(mSatelliteController).getRequestIsEmergency();
         doReturn(1).when(mSatelliteController).getSelectedSatelliteSubId();
-        SubscriptionManagerService isub = mock(SubscriptionManagerService.class);
-        replaceInstance(SubscriptionManagerService.class, "sInstance", null, isub);
         SubscriptionInfoInternal info = mock(SubscriptionInfoInternal.class);
-        doReturn(info).when(isub).getSubscriptionInfoInternal(1);
+        doReturn(info).when(mSubscriptionManagerService).getSubscriptionInfoInternal(1);
 
         // Carrier: shouldTurnOffCarrierSatelliteForEmergencyCall = false
         doReturn(0).when(info).getOnlyNonTerrestrialNetwork();
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
index c659d5e..c695374 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
@@ -32,6 +32,7 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.TelephonyTestBase;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.d2d.DtmfTransport;
@@ -43,12 +44,11 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
 
 @RunWith(AndroidJUnit4.class)
-public class TelephonyConnectionTest {
+public class TelephonyConnectionTest extends TelephonyTestBase {
     @Mock
     private ImsPhoneConnection mImsPhoneConnection;
     @Mock
@@ -56,7 +56,8 @@
 
     @Before
     public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
+        super.setUp();
+
         when(mImsPhoneConnection.getState()).thenReturn(Call.State.ACTIVE);
         when(mImsPhoneConnection.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_IMS);
     }
diff --git a/tests/src/com/android/services/telephony/rcs/SipSessionTrackerTest.java b/tests/src/com/android/services/telephony/rcs/SipSessionTrackerTest.java
index 3874321..dfe96ef 100644
--- a/tests/src/com/android/services/telephony/rcs/SipSessionTrackerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/SipSessionTrackerTest.java
@@ -43,6 +43,7 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.TelephonyTestBase;
 import com.android.internal.telephony.ISipDialogStateCallback;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.PhoneFactory;
@@ -64,7 +65,7 @@
 import java.util.stream.Collectors;
 
 @RunWith(AndroidJUnit4.class)
-public class SipSessionTrackerTest {
+public class SipSessionTrackerTest extends TelephonyTestBase {
 
     private class DialogAttributes {
         public final String branchId;
@@ -133,6 +134,8 @@
 
     @Before
     public void setUp() throws Exception {
+        super.setUp();
+
         mStringEntryCounter = 0;
         MockitoAnnotations.initMocks(this);
         mTrackerUT = new SipSessionTracker(TEST_SUB_ID, mRcsStats);
diff --git a/tests/src/com/android/services/telephony/rcs/SipTransportControllerTest.java b/tests/src/com/android/services/telephony/rcs/SipTransportControllerTest.java
index df7a37e..4ec5e62 100644
--- a/tests/src/com/android/services/telephony/rcs/SipTransportControllerTest.java
+++ b/tests/src/com/android/services/telephony/rcs/SipTransportControllerTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeNotNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -717,7 +718,9 @@
     @SmallTest
     @Test
     public void testFeatureTagsDeniedByOverride() throws Exception {
-        RcsProvisioningMonitor.getInstance().overrideImsFeatureValidation(TEST_SUB_ID, false);
+        RcsProvisioningMonitor monitor = RcsProvisioningMonitor.getInstance();
+        assumeNotNull(monitor);
+        monitor.overrideImsFeatureValidation(TEST_SUB_ID, false);
         SipTransportController controller = setupLiveTransportController(THROTTLE_MS, 0);
 
         ArraySet<String> requestTags = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
@@ -737,8 +740,10 @@
     @SmallTest
     @Test
     public void testFeatureTagsDeniedByConfigAllowedByOverride() throws Exception {
+        RcsProvisioningMonitor monitor = RcsProvisioningMonitor.getInstance();
+        assumeNotNull(monitor);
         setFeatureAllowedConfig(TEST_SUB_ID, new String[]{});
-        RcsProvisioningMonitor.getInstance().overrideImsFeatureValidation(TEST_SUB_ID, true);
+        monitor.overrideImsFeatureValidation(TEST_SUB_ID, true);
         SipTransportController controller = setupLiveTransportController(THROTTLE_MS, 0);
 
         ArraySet<String> requestTags = new ArraySet<>(getBaseDelegateRequest().getFeatureTags());
diff --git a/utils/satellite/tools/src/main/java/com/android/telephony/tools/sats2/SatS2LocationLookup.java b/utils/satellite/tools/src/main/java/com/android/telephony/tools/sats2/SatS2LocationLookup.java
index 4632233..3a0dfd2 100644
--- a/utils/satellite/tools/src/main/java/com/android/telephony/tools/sats2/SatS2LocationLookup.java
+++ b/utils/satellite/tools/src/main/java/com/android/telephony/tools/sats2/SatS2LocationLookup.java
@@ -24,10 +24,21 @@
 import com.google.common.geometry.S2CellId;
 import com.google.common.geometry.S2LatLng;
 
+import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /** A util class for checking if a location is in the input satellite S2 file. */
 public final class SatS2LocationLookup {
+
+    private static final Pattern DMS_PATTERN =
+            Pattern.compile(
+                    "^\"?(\\d+)°(\\d+)'(\\d+)\"+\\s*(N|S)\\s+(\\d+)°(\\d+)'(\\d+)\"+\\s*(E|W)\"?$");
+
     /**
      *  A util method for checking if a location is in the input satellite S2 file.
      */
@@ -38,17 +49,39 @@
                 .build()
                 .parse(args);
 
+        if (arguments.csvFile != null) {
+            processLocationLookupFromCSV(arguments);
+            return;
+        }
+
+        // Make sure either DMS or DD format location is passed
+        if (arguments.dms == null && arguments.latDegrees == null && arguments.lngDegrees == null) {
+            throw new IllegalArgumentException(
+                    "Either --lat-degrees and --lng-degrees or --dms must be specified");
+        }
+
+        double latDegrees, lngDegrees;
+
+        if (arguments.dms != null) {
+            double[] dmsCoords = parseDMS(arguments.dms);
+            latDegrees = dmsCoords[0];
+            lngDegrees = dmsCoords[1];
+        } else {
+            latDegrees = arguments.latDegrees;
+            lngDegrees = arguments.lngDegrees;
+        }
+
         try (SatS2RangeFileReader satS2RangeFileReader =
                      SatS2RangeFileReader.open(new File(arguments.inputFile))) {
             System.out.println(
                     "lat - "
-                            + arguments.latDegrees
+                            + latDegrees
                             + ", long - "
-                            + arguments.lngDegrees
+                            + lngDegrees
                             + ", s2Level - "
                             + satS2RangeFileReader.getS2Level());
-            S2CellId s2CellId = getS2CellId(arguments.latDegrees, arguments.lngDegrees,
-                    satS2RangeFileReader.getS2Level());
+            S2CellId s2CellId =
+                    getS2CellId(latDegrees, lngDegrees, satS2RangeFileReader.getS2Level());
             System.out.println("s2CellId=" + Long.toUnsignedString(s2CellId.id())
                     + ", token=" + s2CellId.toToken());
             SuffixTableRange entry = satS2RangeFileReader.findEntryByCellId(s2CellId.id());
@@ -61,6 +94,84 @@
         }
     }
 
+    private static void processLocationLookupFromCSV(Arguments arguments) throws Exception {
+        File inputFile = new File(arguments.inputFile);
+        File csvFile = new File(arguments.csvFile);
+        File outputFile = new File(arguments.outputFile);
+
+        try (SatS2RangeFileReader satS2RangeFileReader =
+                        SatS2RangeFileReader.open(new File(arguments.inputFile));
+                BufferedReader csvReader = new BufferedReader(new FileReader(arguments.csvFile));
+                FileWriter csvWriter = new FileWriter(arguments.outputFile)) {
+
+            // Write header to output CSV
+            csvWriter.append("Place,Distance,DMS coordinates,Satellite supported\n");
+
+            String row = csvReader.readLine(); // skip first row
+            while ((row = csvReader.readLine()) != null) {
+                String[] data = row.split(",");
+                if (data.length != 3) { // Handle invalid CSV rows
+                    System.err.println("Skipping invalid CSV row: " + row);
+                    continue;
+                }
+
+                String place = data[0].trim();
+                String distance = data[1].trim();
+                String dms = data[2].trim();
+
+                // Remove the outer double quotes if present, but keep inner quotes:
+                String cleanedDMS = dms.replaceAll("^\"", "").replaceAll("\"$", "").trim();
+
+                double[] dmsCoords = parseDMS(cleanedDMS);
+                double latDegrees = dmsCoords[0];
+                double lngDegrees = dmsCoords[1];
+
+                S2CellId s2CellId =
+                        getS2CellId(latDegrees, lngDegrees, satS2RangeFileReader.getS2Level());
+                SuffixTableRange entry = satS2RangeFileReader.findEntryByCellId(s2CellId.id());
+
+                String supported = (entry != null) ? "Yes" : "No";
+
+                // Write data to the output file
+                csvWriter.append(String.format("%s,%s,%s,%s\n", place, distance, dms, supported));
+
+                System.out.println(String.format("%s,%s,%s,%s\n", place, distance, dms, supported));
+            }
+
+        } catch (IOException e) {
+            System.err.println("Error processing CSV file: " + e.getMessage());
+            throw e;
+        }
+
+        System.out.println("Geofence lookup results are at: " + outputFile.getAbsolutePath());
+    }
+
+    private static double[] parseDMS(String dmsString) {
+        Matcher matcher = DMS_PATTERN.matcher(dmsString);
+        if (!matcher.matches()) {
+            System.err.println("Invalid DMS format: " + dmsString);
+            throw new IllegalArgumentException("Invalid DMS format: " + dmsString);
+        }
+
+        double latDegrees =
+                Integer.parseInt(matcher.group(1))
+                        + Integer.parseInt(matcher.group(2)) / 60.0
+                        + Integer.parseInt(matcher.group(3)) / 3600.0;
+        if (matcher.group(4).equals("S")) {
+            latDegrees = -latDegrees;
+        }
+
+        double lngDegrees =
+                Integer.parseInt(matcher.group(5))
+                        + Integer.parseInt(matcher.group(6)) / 60.0
+                        + Integer.parseInt(matcher.group(7)) / 3600.0;
+        if (matcher.group(8).equals("W")) {
+            lngDegrees = -lngDegrees;
+        }
+
+        return new double[] {latDegrees, lngDegrees};
+    }
+
     private static S2CellId getS2CellId(double latDegrees, double lngDegrees, int s2Level) {
         // Create the leaf S2 cell containing the given S2LatLng
         S2CellId cellId = S2CellId.fromLatLng(S2LatLng.fromDegrees(latDegrees, lngDegrees));
@@ -75,14 +186,21 @@
                 required = true)
         public String inputFile;
 
-        @Parameter(names = "--lat-degrees",
-                description = "lat degress of the location",
-                required = true)
-        public double latDegrees;
+        @Parameter(names = "--lat-degrees", description = "latitude in degrees")
+        public Double latDegrees;
 
-        @Parameter(names = "--lng-degrees",
-                description = "lng degress of the location",
-                required = true)
-        public double lngDegrees;
+        @Parameter(names = "--lng-degrees", description = "longitude in degrees")
+        public Double lngDegrees;
+
+        @Parameter(
+                names = "--dms",
+                description = "coordinates in DMS format (e.g., 32°43'19\"N 117°23'40\"W)")
+        public String dms;
+
+        @Parameter(names = "--csv-file", description = "Input CSV file")
+        public String csvFile;
+
+        @Parameter(names = "--output-file", description = "Output CSV file")
+        public String outputFile;
     }
 }