Only send calls when the InCallService is non-null

Bug Reports came in regarding the InCallController sending calls to a
null InCallService binding.  A null pointer exception was thrown and
which lead to a failure in adding the outgoing meetings.

To prevent the calls from failing, Telecom should verify the
InCallService binding is non-null before sending the new call over to
the InCallService.  In the event the InCallService is null, an amomaly
report is generated to further investigate when this situation arises
and the fequency.

Flag: com.android.server.telecom.flags.do_not_send_call_to_null_ics
Bug: 345473659
Test: unit
Change-Id: I5b335596a1918575472bafad11cfc3c6408ec1a8
diff --git a/flags/telecom_incallservice_flags.aconfig b/flags/telecom_incallservice_flags.aconfig
index d1236e0..c95816a 100644
--- a/flags/telecom_incallservice_flags.aconfig
+++ b/flags/telecom_incallservice_flags.aconfig
@@ -32,3 +32,11 @@
   description: "Ensure onCallEndpointChanged is sent to ICS when it connects."
   bug: "348297436"
 }
+
+# OWNER=tjstuart TARGET=24Q4
+flag {
+  name: "do_not_send_call_to_null_ics"
+  namespace: "telecom"
+  description: "Only send calls to the InCallService if the binding is not null"
+  bug: "345473659"
+}
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 72d982b..6164638 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -103,7 +103,10 @@
             UUID.fromString("0c2adf96-353a-433c-afe9-1e5564f304f9");
     public static final String SET_IN_CALL_ADAPTER_ERROR_MSG =
             "Exception thrown while setting the in-call adapter.";
-
+    public static final UUID NULL_IN_CALL_SERVICE_BINDING_UUID =
+            UUID.fromString("7d58dedf-b71d-4c18-9d23-47b434bde58b");
+    public static final String NULL_IN_CALL_SERVICE_BINDING_ERROR_MSG =
+            "InCallController#sendCallToInCallService with null InCallService binding";
     @VisibleForTesting
     public void setAnomalyReporterAdapter(AnomalyReporterAdapter mAnomalyReporterAdapter){
         mAnomalyReporter = mAnomalyReporterAdapter;
@@ -2602,7 +2605,8 @@
         return true;
     }
 
-    private int sendCallToService(Call call, InCallServiceInfo info,
+    @VisibleForTesting
+    public int sendCallToService(Call call, InCallServiceInfo info,
             IInCallService inCallService) {
         try {
             if ((call.isSelfManaged() && (!info.isSelfManagedCallsSupported()
@@ -2628,7 +2632,20 @@
                     includeRttCall,
                     info.getType() == IN_CALL_SERVICE_TYPE_SYSTEM_UI ||
                             info.getType() == IN_CALL_SERVICE_TYPE_NON_UI);
-            inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
+            if (mFeatureFlags.doNotSendCallToNullIcs()) {
+                if (inCallService != null) {
+                    inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
+                } else {
+                    Log.w(this, "call=[%s], was not sent to InCallService"
+                                    + " with info=[%s] due to a null InCallService binding",
+                            call, info);
+                    mAnomalyReporter.reportAnomaly(NULL_IN_CALL_SERVICE_BINDING_UUID,
+                            NULL_IN_CALL_SERVICE_BINDING_ERROR_MSG);
+                    return 0;
+                }
+            } else {
+                inCallService.addCall(sanitizeParcelableCallForService(info, parcelableCall));
+            }
             updateCallTracking(call, info, true /* isAdd */);
             return 1;
         } catch (RemoteException ignored) {
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index a6cad71..449aa41 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -1946,6 +1946,25 @@
         when(mFeatureFlags.profileUserSupport()).thenReturn(true);
     }
 
+    /**
+     * Verify that if a null inCallService object is passed to sendCallToInCallService, a
+     * NullPointerException is not thrown.
+     */
+    @Test
+    public void testSendCallToInCallServiceWithNullService() {
+        when(mFeatureFlags.doNotSendCallToNullIcs()).thenReturn(true);
+        //Setup up parent and child/work profile relation
+        when(mMockChildUserCall.getAssociatedUser()).thenReturn(mChildUserHandle);
+        when(mMockCallsManager.getCurrentUserHandle()).thenReturn(mParentUserHandle);
+        when(mMockUserManager.getProfileParent(mChildUserHandle)).thenReturn(mParentUserHandle);
+        when(mFeatureFlags.profileUserSupport()).thenReturn(true);
+        when(mMockContext.getSystemService(eq(UserManager.class)))
+                .thenReturn(mMockUserManager);
+        // verify a NullPointerException is not thrown
+        int res = mInCallController.sendCallToService(mMockCall, mInCallServiceInfo, null);
+        assertEquals(0, res);
+    }
+
     @Test
     public void testProfileCallQueriesIcsUsingParentUserToo() throws Exception {
         setupMocksForProfileTest();