Merge "Fix crashes with partial telephony stack after boot" into main
diff --git a/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java b/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
index ce77a92..de3811a 100644
--- a/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
+++ b/src/com/android/phone/satellite/accesscontrol/SatelliteAccessController.java
@@ -25,6 +25,10 @@
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_REQUEST_NOT_SUPPORTED;
 import static android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS;
 
+import static com.android.internal.telephony.satellite.SatelliteConstants.TRIGGERING_EVENT_EXTERNAL_REQUEST;
+import static com.android.internal.telephony.satellite.SatelliteConstants.TRIGGERING_EVENT_LOCATION_SETTINGS_ENABLED;
+import static com.android.internal.telephony.satellite.SatelliteConstants.TRIGGERING_EVENT_MCC_CHANGED;
+import static com.android.internal.telephony.satellite.SatelliteConstants.TRIGGERING_EVENT_UNKNOWN;
 import static com.android.internal.telephony.satellite.SatelliteController.SATELLITE_SHARED_PREF;
 
 import android.annotation.ArrayRes;
@@ -402,6 +406,7 @@
                         + mCurrentSatelliteAllowedState);
                 mCurrentSatelliteAllowedState = isAllowed;
                 notifySatelliteCommunicationAllowedStateChanged(isAllowed);
+                mControllerMetricsStats.reportAllowedStateChanged();
             }
         }
     }
@@ -441,7 +446,7 @@
             case EVENT_LOCATION_SETTINGS_ENABLED:
                 // Fall through
             case EVENT_COUNTRY_CODE_CHANGED:
-                handleSatelliteAllowedRegionPossiblyChanged();
+                handleSatelliteAllowedRegionPossiblyChanged(msg.what);
                 break;
             default:
                 plogw("SatelliteAccessControllerHandler: unexpected message code: " + msg.what);
@@ -463,6 +468,7 @@
             result.send(SATELLITE_RESULT_REQUEST_NOT_SUPPORTED, null);
             return;
         }
+        mAccessControllerMetricsStats.setTriggeringEvent(TRIGGERING_EVENT_EXTERNAL_REQUEST);
         sendRequestAsync(CMD_IS_SATELLITE_COMMUNICATION_ALLOWED,
                 new Pair<>(mSatelliteController.getSatellitePhone().getSubId(), result));
     }
@@ -1114,7 +1120,7 @@
         };
     }
 
-    private void handleSatelliteAllowedRegionPossiblyChanged() {
+    private void handleSatelliteAllowedRegionPossiblyChanged(int handleEvent) {
         if (!mFeatureFlags.oemEnabledSatelliteFlag()) {
             ploge("handleSatelliteAllowedRegionPossiblyChanged: "
                     + "The feature flag oemEnabledSatelliteFlag() is not enabled");
@@ -1125,6 +1131,13 @@
             setIsSatelliteAllowedRegionPossiblyChanged(true);
             requestIsCommunicationAllowedForCurrentLocation(
                     mHandlerForSatelliteAllowedResult);
+            int triggeringEvent = TRIGGERING_EVENT_UNKNOWN;
+            if (handleEvent == EVENT_LOCATION_SETTINGS_ENABLED) {
+                triggeringEvent = TRIGGERING_EVENT_LOCATION_SETTINGS_ENABLED;
+            } else if (handleEvent == EVENT_COUNTRY_CODE_CHANGED) {
+                triggeringEvent = TRIGGERING_EVENT_MCC_CHANGED;
+            }
+            mAccessControllerMetricsStats.setTriggeringEvent(triggeringEvent);
         }
     }
 
@@ -1324,6 +1337,7 @@
                 }
                 mAccessControllerMetricsStats.setAccessControlType(
                         SatelliteConstants.ACCESS_CONTROL_TYPE_CURRENT_LOCATION);
+                mControllerMetricsStats.reportLocationQuerySuccessful(true);
                 checkSatelliteAccessRestrictionForLocation(location);
             } else {
                 plogd("current location is not available");
@@ -1338,6 +1352,7 @@
                     sendSatelliteAllowResultToReceivers(
                             SATELLITE_RESULT_LOCATION_NOT_AVAILABLE, bundle, false);
                 }
+                mControllerMetricsStats.reportLocationQuerySuccessful(false);
             }
         }
     }
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 3a52406..e44dcef 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -18,6 +18,8 @@
 
 import static android.telephony.CarrierConfigManager.KEY_USE_ONLY_DIALED_SIM_ECC_LIST_BOOL;
 import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
+import static android.telephony.ServiceState.STATE_EMERGENCY_ONLY;
+import static android.telephony.ServiceState.STATE_IN_SERVICE;
 import static android.telephony.TelephonyManager.HAL_SERVICE_VOICE;
 
 import static com.android.internal.telephony.PhoneConstants.PHONE_TYPE_GSM;
@@ -1156,7 +1158,7 @@
             // so they will only need the special emergency call setup when the phone is out of
             // service.
             if (phone == null || phone.getServiceState().getState()
-                    != ServiceState.STATE_IN_SERVICE) {
+                    != STATE_IN_SERVICE) {
                 String convertedNumber = mPhoneNumberUtilsProxy.convertToEmergencyNumber(this,
                         number);
                 if (!TextUtils.equals(convertedNumber, number)) {
@@ -1297,7 +1299,7 @@
                         return phone.getState() == PhoneConstants.State.OFFHOOK
                                 // Do not wait for voice in service on opportunistic SIMs.
                                 || subInfo != null && subInfo.isOpportunistic()
-                                || (serviceState == ServiceState.STATE_IN_SERVICE
+                                || (serviceState == STATE_IN_SERVICE
                                 && !needToTurnOffSatellite);
                     }
                 }
@@ -1638,8 +1640,8 @@
 
         if (!isEmergencyNumber) {
             switch (state) {
-                case ServiceState.STATE_IN_SERVICE:
-                case ServiceState.STATE_EMERGENCY_ONLY:
+                case STATE_IN_SERVICE:
+                case STATE_EMERGENCY_ONLY:
                     break;
                 case ServiceState.STATE_OUT_OF_SERVICE:
                     if (phone.isUtEnabled() && number.endsWith("#")) {
@@ -2137,7 +2139,7 @@
 
         return imsPhone != null
                 && (imsPhone.isVoiceOverCellularImsEnabled() || imsPhone.isWifiCallingEnabled())
-                && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE);
+                && (imsPhone.getServiceState().getState() == STATE_IN_SERVICE);
     }
 
     private boolean isRadioOn() {
@@ -3069,7 +3071,7 @@
         }
 
         ServiceState ss = phone.getServiceStateTracker().getServiceState();
-        if (ss.getState() != ServiceState.STATE_IN_SERVICE) return false;
+        if (ss.getState() != STATE_IN_SERVICE) return false;
 
         NetworkRegistrationInfo regState = ss.getNetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
@@ -4143,7 +4145,7 @@
     @VisibleForTesting
     public boolean isAvailableForEmergencyCalls(Phone phone,
             @EmergencyNumber.EmergencyCallRouting int routing) {
-        if (isCallDisallowedDueToSatellite(phone)) {
+        if (isCallDisallowedDueToSatellite(phone) && isTerrestrialNetworkAvailable()) {
             // Phone is connected to satellite due to which it is not preferred for emergency call.
             return false;
         }
@@ -4157,7 +4159,7 @@
         }
 
         // In service phones are always appropriate for emergency calls.
-        if (ServiceState.STATE_IN_SERVICE == phone.getServiceState().getState()) {
+        if (STATE_IN_SERVICE == phone.getServiceState().getState()) {
             return true;
         }
 
@@ -4168,6 +4170,23 @@
                 && phone.getServiceState().isEmergencyOnly());
     }
 
+    private boolean isTerrestrialNetworkAvailable() {
+        for (Phone phone : mPhoneFactoryProxy.getPhones()) {
+            ServiceState serviceState = phone.getServiceState();
+            if (serviceState != null) {
+                int state = serviceState.getState();
+                if ((state == STATE_IN_SERVICE || state == STATE_EMERGENCY_ONLY
+                        || serviceState.isEmergencyOnly())
+                        && !serviceState.isUsingNonTerrestrialNetwork()) {
+                    Log.d(this, "isTerrestrialNetworkAvailable true");
+                    return true;
+                }
+            }
+        }
+        Log.d(this, "isTerrestrialNetworkAvailable false");
+        return false;
+    }
+
     /**
      * Determines if the connection should allow mute.
      *
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 6990e29..6eb9416 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -3888,6 +3888,7 @@
         ss.setEmergencyOnly(true);
         ss.setState(ServiceState.STATE_EMERGENCY_ONLY);
         when(mockPhone.getServiceState()).thenReturn(ss);
+        when(mPhoneFactoryProxy.getPhones()).thenReturn(new Phone[] {mockPhone});
 
         assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
                 EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY));
@@ -3911,6 +3912,79 @@
         ss.setState(ServiceState.STATE_EMERGENCY_ONLY);
         when(mockPhone.getServiceState()).thenReturn(ss);
 
+        when(mPhoneFactoryProxy.getPhones()).thenReturn(new Phone[] {mockPhone});
+
+        assertTrue(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY));
+        assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL));
+        assertTrue(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
+    }
+
+    @Test
+    public void testIsAvailableForEmergencyCallsUsingNTN_CellularAvailable() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG);
+
+        // Call is not supported while using satellite
+        when(mSatelliteController.isInSatelliteModeForCarrierRoaming(any())).thenReturn(true);
+        when(mSatelliteController.getCapabilitiesForCarrierRoamingSatelliteMode(any()))
+                .thenReturn(List.of(NetworkRegistrationInfo.SERVICE_TYPE_DATA));
+
+        Phone mockPhone = Mockito.mock(Phone.class);
+        ServiceState ss = new ServiceState();
+        ss.setEmergencyOnly(true);
+        ss.setState(ServiceState.STATE_EMERGENCY_ONLY);
+        when(mockPhone.getServiceState()).thenReturn(ss);
+
+        // Phone2 is in limited service
+        Phone mockPhone2 = Mockito.mock(Phone.class);
+        ServiceState ss2 = new ServiceState();
+        ss2.setEmergencyOnly(true);
+        ss2.setState(ServiceState.STATE_EMERGENCY_ONLY);
+        when(mockPhone2.getServiceState()).thenReturn(ss2);
+
+        Phone[] phones = {mockPhone, mockPhone2};
+        when(mPhoneFactoryProxy.getPhones()).thenReturn(phones);
+
+        assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY));
+        assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL));
+        assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
+                EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN));
+    }
+
+    @Test
+    public void testIsAvailableForEmergencyCallsUsingNTN_CellularNotAvailable() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG);
+
+        // Call is not supported while using satellite
+        when(mSatelliteController.isInSatelliteModeForCarrierRoaming(any())).thenReturn(true);
+        when(mSatelliteController.getCapabilitiesForCarrierRoamingSatelliteMode(any()))
+                .thenReturn(List.of(NetworkRegistrationInfo.SERVICE_TYPE_DATA));
+
+        NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+                .setIsNonTerrestrialNetwork(true)
+                .setAvailableServices(List.of(NetworkRegistrationInfo.SERVICE_TYPE_DATA))
+                .build();
+        Phone mockPhone = Mockito.mock(Phone.class);
+        ServiceState ss = new ServiceState();
+        ss.addNetworkRegistrationInfo(nri);
+        ss.setEmergencyOnly(true);
+        ss.setState(ServiceState.STATE_EMERGENCY_ONLY);
+        when(mockPhone.getServiceState()).thenReturn(ss);
+
+        // Phone2 is out of service
+        Phone mockPhone2 = Mockito.mock(Phone.class);
+        ServiceState ss2 = new ServiceState();
+        ss2.setEmergencyOnly(false);
+        ss2.setState(ServiceState.STATE_OUT_OF_SERVICE);
+        when(mockPhone2.getServiceState()).thenReturn(ss2);
+
+        Phone[] phones = {mockPhone, mockPhone2};
+        when(mPhoneFactoryProxy.getPhones()).thenReturn(phones);
+
         assertTrue(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,
                 EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY));
         assertFalse(mTestConnectionService.isAvailableForEmergencyCalls(mockPhone,