Enforce the subId is active and setup on RCS features before allowing IPC

1) When accessing the RCS APIs, we now enforce that the subId matches
the loaded and configured subId to remove race conditions where an
app calls an RCS api before the RCS feature is set up fully on the
framework side.

2) Send an explicit Exception when using isSupported/isAvailable APIs.

Bug: 218893458
Test: atest TeleServiceTests
Change-Id: Idd181dbfe4b10a2b720ca04d81b578f8158528ea
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.