[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into tm-dev am: 16abcdf3c3 -s ours am: 972a90d7d8 -s ours am: 89a7cd5915 -s ours

am skip reason: subject contains skip directive

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/services/Telephony/+/17785727

Change-Id: Ib5ed9a70567a0962eaca2821821434856f341dd3
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/src/com/android/phone/ImsProvisioningController.java b/src/com/android/phone/ImsProvisioningController.java
index d78e565..54fc547 100644
--- a/src/com/android/phone/ImsProvisioningController.java
+++ b/src/com/android/phone/ImsProvisioningController.java
@@ -16,6 +16,8 @@
 
 package com.android.phone;
 
+import static android.telephony.ims.ImsRcsManager.CAPABILITY_TYPE_OPTIONS_UCE;
+import static android.telephony.ims.ImsRcsManager.CAPABILITY_TYPE_PRESENCE_UCE;
 import static android.telephony.ims.ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
 import static android.telephony.ims.ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
 import static android.telephony.ims.ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
@@ -29,8 +31,6 @@
 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT;
 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO;
 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
-import static android.telephony.ims.feature.RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE;
-import static android.telephony.ims.feature.RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
@@ -202,7 +202,7 @@
     /**
      * This class contains the provisioning status to notify changes.
      * {{@link MmTelCapabilities.MmTelCapability} for MMTel services}
-     * {{@link RcsImsCapabilities.RcsImsCapabilityFlag} for RCS services}
+     * {{@link android.telephony.ims.ImsRcsManager.RcsImsCapabilityFlag} for RCS services}
      * {{@link ImsRegistrationImplBase.ImsRegistrationTech} for Registration tech}
      */
     private static final class FeatureProvisioningData {
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index bf55764..d4a0f1e 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -252,7 +252,7 @@
         } catch (ImsException e) {
             Log.e(TAG, "isCapable: sudId=" + subId
                     + ", capability=" + capability + ", " + e.getMessage());
-            return false;
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -278,7 +278,7 @@
         } catch (ImsException e) {
             Log.e(TAG, "isAvailable: sudId=" + subId
                     + ", capability=" + capability + ", " + e.getMessage());
-            return false;
+            throw new ServiceSpecificException(e.getCode(), e.getMessage());
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -786,16 +786,43 @@
         int slotId = phone.getPhoneId();
         if (!skipVerifyingConfig) {
             verifyImsRcsConfiguredOrThrow(slotId);
+            verifyRcsSubIdActiveOrThrow(slotId, subId);
         }
         RcsFeatureController c = mRcsService.getFeatureController(slotId);
         if (c == null) {
+            // If we hit this case, we have verified that TelephonyRcsService has processed any
+            // subId changes for the associated slot and applied configs. In this case, the configs
+            // do not have the RCS feature enabled.
             throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
                     "The requested operation is not supported for subId " + subId);
         }
+        if (!skipVerifyingConfig && c.getAssociatedSubId() != subId) {
+            // If we hit this case, the ImsFeature has not finished setting up the RCS feature yet
+            // or the RCS feature has crashed and is being set up again.
+            Log.w(TAG, "getRcsFeatureController: service unavailable on slot " + slotId
+                    + " for subId " + subId);
+            throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
+                    "The ImsService is not currently available for subid " + subId
+                            + ", please try again");
+        }
         return c;
     }
 
     /**
+     * Ensure the TelephonyRcsService is tracking the supplied subId for the supplied slotId and has
+     * set up the stack.
+     */
+    private void verifyRcsSubIdActiveOrThrow(int slotId, int subId) {
+        if (mRcsService.verifyActiveSubId(slotId, subId)) return;
+
+        Log.w(TAG, "verifyRcsSubIdActiveOrThrow: verify failed, service not set up yet on "
+                + "slot " + slotId + " for subId " + subId);
+        throw new ServiceSpecificException(ImsException.CODE_ERROR_SERVICE_UNAVAILABLE,
+                "ImsService set up in progress for subId " + subId
+                        + ", please try again");
+    }
+
+    /**
      * Throw an ImsException if the IMS resolver does not have an ImsService configured for RCS
      * for the given slot ID or no ImsResolver instance has been created.
      * @param slotId The slot ID that the IMS service is created for.
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index ed07726..684e03a 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -2016,32 +2016,47 @@
     }
 
     @VisibleForTesting
-    public PersistableBundle getCarrierConfig() {
+    public @NonNull PersistableBundle getCarrierConfig() {
         Phone phone = getPhone();
         if (phone == null) {
-            return null;
+            Log.w(this,
+                    "getCarrierConfig: phone is null. Returning CarrierConfigManager"
+                            + ".getDefaultConfig()");
+            return CarrierConfigManager.getDefaultConfig();
         }
-        return PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
+
+        // potential null returned from .getCarrierConfigForSubId() and method guarantees non-null.
+        // hence, need for try/finally block
+        PersistableBundle pb = null;
+        try {
+            pb = PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
+        } catch (Exception e) {
+            Log.e(this, e,
+                    "getCarrierConfig: caught Exception when calling "
+                            + "PhoneGlobals.getCarrierConfigForSubId(phone.getSubId()). Returning "
+                            + "CarrierConfigManager.getDefaultConfig()");
+        } finally {
+            if (pb == null) {
+                pb = CarrierConfigManager.getDefaultConfig();
+            }
+        }
+        return pb;
+    }
+
+    @VisibleForTesting
+    public boolean isRttMergeSupported(@NonNull PersistableBundle pb) {
+        return pb.getBoolean(CarrierConfigManager.KEY_ALLOW_MERGING_RTT_CALLS_BOOL);
     }
 
     private boolean canDeflectImsCalls() {
-        PersistableBundle b = getCarrierConfig();
-        // Return false if the CarrierConfig is unavailable
-        if (b != null) {
-            return b.getBoolean(
-                    CarrierConfigManager.KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL) &&
-                    isValidRingingCall();
-        }
-        return false;
+        return getCarrierConfig().getBoolean(
+                CarrierConfigManager.KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL)
+                && isValidRingingCall();
     }
 
     private boolean isCallTransferSupported() {
-        PersistableBundle b = getCarrierConfig();
-        // Return false if the CarrierConfig is unavailable
-        if (b != null) {
-            return b.getBoolean(CarrierConfigManager.KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL);
-        }
-        return false;
+        return getCarrierConfig().getBoolean(
+                CarrierConfigManager.KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL);
     }
 
     private boolean canTransfer(TelephonyConnection c) {
@@ -3038,8 +3053,6 @@
         if (isIms) {
             isVoWifiEnabled = isWfcEnabled(phone);
         }
-        boolean isRttMergeSupported = getCarrierConfig()
-                .getBoolean(CarrierConfigManager.KEY_ALLOW_MERGING_RTT_CALLS_BOOL);
         PhoneAccountHandle phoneAccountHandle = isIms ? PhoneUtils
                 .makePstnPhoneAccountHandle(phone.getDefaultPhone())
                 : PhoneUtils.makePstnPhoneAccountHandle(phone);
@@ -3077,7 +3090,7 @@
         if (mTreatAsEmergencyCall) {
             isConferenceSupported = false;
             Log.d(this, "refreshConferenceSupported = false; emergency call");
-        } else if (isRtt() && !isRttMergeSupported) {
+        } else if (isRtt() && !isRttMergeSupported(getCarrierConfig())) {
             isConferenceSupported = false;
             Log.d(this, "refreshConferenceSupported = false; rtt call");
         } else if (!isConferencingSupported || isIms && !isImsConferencingSupported) {
@@ -3134,12 +3147,9 @@
         Phone phone = getPhone();
         if (phone != null && (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA)
                 && !mOriginalConnection.isIncoming()) {
-            PersistableBundle pb = getCarrierConfig();
-            if (pb != null) {
-                showOrigDialString = pb.getBoolean(CarrierConfigManager
-                        .KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL);
-                Log.d(this, "showOrigDialString: " + showOrigDialString);
-            }
+            showOrigDialString = getCarrierConfig().getBoolean(CarrierConfigManager
+                    .KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL);
+            Log.d(this, "showOrigDialString: " + showOrigDialString);
         }
         return showOrigDialString;
     }
@@ -3716,8 +3726,7 @@
         if (mOriginalConnection.isIncoming()
                 && !TextUtils.isEmpty(mOriginalConnection.getAddress())
                 && mOriginalConnection.getAddress().startsWith(JAPAN_COUNTRY_CODE_WITH_PLUS_SIGN)) {
-            PersistableBundle b = getCarrierConfig();
-            return b != null && b.getBoolean(
+            return getCarrierConfig().getBoolean(
                     CarrierConfigManager.KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL);
         }
         return false;
@@ -3742,8 +3751,7 @@
      * otherwise.
      */
     private boolean supportsD2DUsingRtp() {
-        PersistableBundle b = getCarrierConfig();
-        return b != null && b.getBoolean(
+        return getCarrierConfig().getBoolean(
                 CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL);
     }
 
@@ -3751,8 +3759,7 @@
      * @return {@code true} if the carrier supports D2D using DTMF digits, {@code false} otherwise.
      */
     private boolean supportsD2DUsingDtmf() {
-        PersistableBundle b = getCarrierConfig();
-        return b != null && b.getBoolean(
+        return getCarrierConfig().getBoolean(
                 CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL);
     }
 
@@ -3761,8 +3768,7 @@
      * extensions used in D2D comms, {@code false} otherwise.
      */
     private boolean supportsSdpNegotiationOfRtpHeaderExtensions() {
-        PersistableBundle b = getCarrierConfig();
-        return b != null && b.getBoolean(
+        return getCarrierConfig().getBoolean(
                 CarrierConfigManager
                         .KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL);
     }
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 071376d..75754ec 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -1834,6 +1834,9 @@
             case CallStateException.ERROR_OTASP_PROVISIONING_IN_PROCESS:
                  cause = android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS;
                  break;
+            case CallStateException.ERROR_FDN_BLOCKED:
+                 cause = android.telephony.DisconnectCause.FDN_BLOCKED;
+                 break;
         }
         connection.setTelephonyConnectionDisconnected(
                 DisconnectCauseUtil.toTelecomDisconnectCause(cause, e.getMessage(),
diff --git a/src/com/android/services/telephony/rcs/RcsFeatureController.java b/src/com/android/services/telephony/rcs/RcsFeatureController.java
index 0e1cb4b..48c84b1 100644
--- a/src/com/android/services/telephony/rcs/RcsFeatureController.java
+++ b/src/com/android/services/telephony/rcs/RcsFeatureController.java
@@ -19,6 +19,7 @@
 import android.annotation.AnyThread;
 import android.content.Context;
 import android.net.Uri;
+import android.telephony.SubscriptionManager;
 import android.telephony.ims.ImsException;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.aidl.IImsCapabilityCallback;
@@ -402,6 +403,17 @@
         callback.accept(mImsRcsRegistrationHelper.getImsRegistrationState());
     }
 
+    /**
+     * @return the subscription ID that is currently associated with this RCS feature.
+     */
+    public int getAssociatedSubId() {
+        RcsFeatureManager manager = getFeatureManager();
+        if (manager != null) {
+            return manager.getSubId();
+        }
+        return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    }
+
     private void updateCapabilities() {
         RcsFeatureManager manager = getFeatureManager();
         if (manager != null) {
diff --git a/src/com/android/services/telephony/rcs/TelephonyRcsService.java b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
index dfcea74..13b3a7d 100644
--- a/src/com/android/services/telephony/rcs/TelephonyRcsService.java
+++ b/src/com/android/services/telephony/rcs/TelephonyRcsService.java
@@ -244,6 +244,22 @@
     }
 
     /**
+     * Verifies the subId supplied is the active subId for the slotId specified.
+     * If we have not processed a CARRIER_CONFIG_CHANGED indication for this subscription yet,
+     * either the subscription is not active or we have not finished setting up the feature yet.
+     * @param slotId The slotId we are verifying
+     * @param subId The subId we are verifying
+     * @return true if the subId is the active subId we are tracking for the slotId specified.
+     */
+    public boolean verifyActiveSubId(int slotId, int subId) {
+        synchronized (mLock) {
+            int currId = mSlotToAssociatedSubIds.get(slotId,
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            return subId == currId;
+        }
+    }
+
+    /**
      * ACTION_CARRIER_CONFIG_CHANGED was received by this service for a specific slot.
      * @param slotId The slotId associated with the event.
      * @param subId The subId associated with the event. May cause the subId associated with the
diff --git a/tests/src/com/android/phone/ImsProvisioningControllerTest.java b/tests/src/com/android/phone/ImsProvisioningControllerTest.java
index c26b6a0..6494df4 100644
--- a/tests/src/com/android/phone/ImsProvisioningControllerTest.java
+++ b/tests/src/com/android/phone/ImsProvisioningControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.phone;
 
+import static android.telephony.ims.ImsRcsManager.CAPABILITY_TYPE_OPTIONS_UCE;
+import static android.telephony.ims.ImsRcsManager.CAPABILITY_TYPE_PRESENCE_UCE;
 import static android.telephony.ims.ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
 import static android.telephony.ims.ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
 import static android.telephony.ims.ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
@@ -28,8 +30,6 @@
 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT;
 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO;
 import static android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE;
-import static android.telephony.ims.feature.RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_OPTIONS_UCE;
-import static android.telephony.ims.feature.RcsFeature.RcsImsCapabilities.CAPABILITY_TYPE_PRESENCE_UCE;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN;
 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_LTE;
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
index 388fd29..c996e5f 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionTest.java
@@ -14,6 +14,7 @@
 import static org.mockito.Mockito.when;
 
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.telecom.Connection;
 import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
@@ -25,6 +26,7 @@
 import com.android.internal.telephony.d2d.DtmfTransport;
 import com.android.internal.telephony.d2d.RtpTransport;
 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
+import com.android.phone.PhoneGlobals;
 import com.android.phone.R;
 
 import org.junit.Before;
@@ -212,4 +214,47 @@
             fail("refreshConferenceSupported threw ClassCastException");
         }
     }
+
+    /**
+     * Tests TelephonyConnection#getCarrierConfig never returns a null given all cases that can
+     * cause a potential null.
+     */
+    @Test
+    public void testGetCarrierConfigBehaviorWithNull() throws Exception {
+        TestTelephonyConnectionSimple c = new TestTelephonyConnectionSimple();
+
+        // case: return a valid carrier config (good case)
+        when(c.mPhoneGlobals.getCarrierConfigForSubId(c.getPhone().getSubId())).
+                thenReturn(CarrierConfigManager.getDefaultConfig());
+        assertNotNull(c.getCarrierConfig());
+
+        // case: PhoneGlobals.getInstance().getCarrierConfigForSubId(int) returns null
+        when(c.mPhoneGlobals.getCarrierConfigForSubId(c.getPhone().getSubId()))
+                .thenReturn(null);
+        assertNotNull(c.getCarrierConfig());
+
+        // case: phone is null
+        c.setMockPhone(null);
+        assertNull(c.getPhone());
+        assertNotNull(c.getCarrierConfig());
+    }
+
+    /**
+     * Tests the behavior of TelephonyConnection#isRttMergeSupported(@NonNull PersistableBundle).
+     * Note, the function should be able to handle an empty PersistableBundle and should NEVER
+     * receive a null object as denoted in by @NonNull annotation.
+     */
+    @Test
+    public void testIsRttMergeSupportedBehavior() {
+        TestTelephonyConnection c = new TestTelephonyConnection();
+        //  ensure isRttMergeSupported(PersistableBundle) does not throw NPE when given an Empty PB
+        assertFalse(c.isRttMergeSupported(new PersistableBundle()));
+
+        // simulate the passing situation
+        c.getCarrierConfigBundle().putBoolean(
+                CarrierConfigManager.KEY_ALLOW_MERGING_RTT_CALLS_BOOL,
+                true);
+        assertTrue(c.isRttMergeSupported(c.getCarrierConfig()));
+    }
+
 }
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnectionSimple.java b/tests/src/com/android/services/telephony/TestTelephonyConnectionSimple.java
new file mode 100644
index 0000000..9dc2551
--- /dev/null
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnectionSimple.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 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.services.telephony;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.AttributionSource;
+import android.content.Context;
+import android.os.Process;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.phone.PhoneGlobals;
+
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class TestTelephonyConnectionSimple extends TelephonyConnection{
+
+    @Mock
+    Context mMockContext;
+
+    @Mock
+    PhoneGlobals mPhoneGlobals;
+
+    private Phone mMockPhone;
+
+    public TelephonyConnection cloneConnection() {
+        return this;
+    }
+
+    public TestTelephonyConnectionSimple(){
+        super(null, null, android.telecom.Call.Details.DIRECTION_INCOMING);
+        MockitoAnnotations.initMocks(this);
+
+        AttributionSource attributionSource = new AttributionSource.Builder(
+                Process.myUid()).build();
+
+        mMockPhone    = mock(Phone.class);
+        mMockContext  = mock(Context.class);
+        mPhoneGlobals = mock(PhoneGlobals.class);
+
+        when(mMockPhone.getSubId()).thenReturn(1);
+    }
+
+    public void setMockPhone(Phone newPhone) {
+        mMockPhone = newPhone;
+    }
+
+    @Override
+    public Phone getPhone() {
+        return mMockPhone;
+    }
+
+}