Merge "Fix an NPE found during CTS testing with setCallerInfo" into main
diff --git a/flags/Android.bp b/flags/Android.bp
index 6d4795b..0e2071b 100644
--- a/flags/Android.bp
+++ b/flags/Android.bp
@@ -24,6 +24,8 @@
     srcs: [
       "telecom_broadcast_flags.aconfig",
       "telecom_ringer_flag_declarations.aconfig",
+      "telecom_api_flags.aconfig",
+      "telecom_incallservice_flags.aconfig"
     ],
 }
 
diff --git a/flags/telecom_api_flags.aconfig b/flags/telecom_api_flags.aconfig
new file mode 100644
index 0000000..e956a38
--- /dev/null
+++ b/flags/telecom_api_flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.telecom.flags"
+
+flag {
+  name: "voip_app_actions_support"
+  namespace: "telecom"
+  description: "When set, Telecom support for additional VOIP application actions is active."
+  bug: "296934278"
+}
\ No newline at end of file
diff --git a/flags/telecom_incallservice_flags.aconfig b/flags/telecom_incallservice_flags.aconfig
new file mode 100644
index 0000000..d70b9cc
--- /dev/null
+++ b/flags/telecom_incallservice_flags.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.server.telecom.flags"
+
+flag {
+  name: "early_binding_to_incall_service"
+  namespace: "telecom"
+  description: "Binds to InCallServices when call requires no call filtering on watch"
+  bug: "282113261"
+}
\ No newline at end of file
diff --git a/proguard.flags b/proguard.flags
index 635eba6..7c71a15 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -9,17 +9,3 @@
 -keep class android.telecom.Log {
   *;
 }
-
-# Keep classes, annotations and members used by Lifecycle. Remove this once aapt2 is enabled
--keepattributes *Annotation*
-
--keep class * implements android.arch.lifecycle.LifecycleObserver {
-}
-
--keep class * implements android.arch.lifecycle.GeneratedAdapter {
-    <init>(...);
-}
-
--keepclassmembers class ** {
-    @android.arch.lifecycle.OnLifecycleEvent *;
-}
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 2cf961d..6b58863 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -101,7 +101,7 @@
     <string name="notification_channel_disconnected_calls" msgid="8228636543997645757">"Αποσυνδεδεμένες κλήσεις"</string>
     <string name="notification_channel_in_call_service_crash" msgid="7313237519166984267">"Εφαρμογές τηλεφώνου που αντιμετώπισαν σφάλμα λειτουργίας"</string>
     <string name="notification_channel_call_streaming" msgid="5100510699787538991">"Ροή κλήσης"</string>
-    <string name="alert_outgoing_call" msgid="5319895109298927431">"Εάν πραγματοποιήσετε αυτήν την κλήση, η κλήση σας μέσω <xliff:g id="OTHER_APP">%1$s</xliff:g> θα τερματιστεί."</string>
+    <string name="alert_outgoing_call" msgid="5319895109298927431">"Εάν πραγματοποιήσετε αυτή την κλήση, η κλήση σας μέσω <xliff:g id="OTHER_APP">%1$s</xliff:g> θα τερματιστεί."</string>
     <string name="alert_redirect_outgoing_call_or_not" msgid="665409645789521636">"Επιλέξτε πώς θα πραγματοποιήσετε την κλήση"</string>
     <string name="alert_place_outgoing_call_with_redirection" msgid="5221065030959024121">"Ανακατεύθυνση της κλήσης μέσω <xliff:g id="OTHER_APP">%1$s</xliff:g>"</string>
     <string name="alert_place_unredirect_outgoing_call" msgid="2467608535225764006">"Κλήση μέσω του αριθμού τηλεφώνου μου"</string>
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
old mode 100644
new mode 100755
index 2544576..92e187c
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -288,11 +288,6 @@
     public static final String EXCEPTION_RETRIEVING_PHONE_ACCOUNTS_EMERGENCY_ERROR_MSG =
             "Exception thrown while retrieving list of potential phone accounts when placing an "
                     + "emergency call.";
-    public static final UUID EMERGENCY_CALL_DISCONNECTED_BEFORE_BEING_ADDED_ERROR_UUID =
-            UUID.fromString("f9a916c8-8d61-4550-9ad3-11c2e84f6364");
-    public static final String EMERGENCY_CALL_DISCONNECTED_BEFORE_BEING_ADDED_ERROR_MSG =
-            "An emergency call was disconnected after the connection was created but before the "
-                    + "call was successfully added to CallsManager.";
     public static final UUID EMERGENCY_CALL_ABORTED_NO_PHONE_ACCOUNTS_ERROR_UUID =
             UUID.fromString("2e994acb-1997-4345-8bf3-bad04303de26");
     public static final String EMERGENCY_CALL_ABORTED_NO_PHONE_ACCOUNTS_ERROR_MSG =
@@ -758,11 +753,10 @@
     @Override
     @VisibleForTesting
     public void onSuccessfulOutgoingCall(Call call, int callState) {
-        Log.v(this, "onSuccessfulOutgoingCall, %s", call);
+        Log.v(this, "onSuccessfulOutgoingCall, call=[%s], state=[%d]", call, callState);
         call.setPostCallPackageName(getRoleManagerAdapter().getDefaultCallScreeningApp(
                 call.getAssociatedUser()));
 
-        setCallState(call, callState, "successful outgoing call");
         if (!mCalls.contains(call)) {
             // Call was not added previously in startOutgoingCall due to it being a potential MMI
             // code, so add it now.
@@ -774,7 +768,13 @@
             listener.onConnectionServiceChanged(call, null, call.getConnectionService());
         }
 
-        markCallAsDialing(call);
+        // Allow the ConnectionService to start the call in the active state. This case is helpful
+        // for conference calls or meetings that can skip the dialing stage.
+        if (callState == CallState.ACTIVE) {
+            setCallState(call, callState, "skipping the dialing state and setting active");
+        } else {
+            markCallAsDialing(call);
+        }
     }
 
     @Override
@@ -1313,7 +1313,7 @@
         return mCallAudioManager;
     }
 
-    InCallController getInCallController() {
+    public InCallController getInCallController() {
         return mInCallController;
     }
 
@@ -2930,6 +2930,10 @@
             call.answer(videoState);
         } else {
             // Hold or disconnect the active call and request call focus for the incoming call.
+            Bundle bundle = new Bundle();
+            bundle.putLong(TelecomManager.EXTRA_CALL_ANSWERED_TIME_MILLIS,
+                     mClockProxy.currentTimeMillis());
+            call.putConnectionServiceExtras(bundle);
             holdActiveCallForNewCall(call);
             mConnectionSvrFocusMgr.requestFocus(
                     call,
@@ -3762,11 +3766,6 @@
         // Notify listeners that the call was disconnected before being added to CallsManager.
         // Listeners will not receive onAdded or onRemoved callbacks.
         if (!mCalls.contains(call)) {
-            if (call.isEmergencyCall()) {
-                mAnomalyReporter.reportAnomaly(
-                        EMERGENCY_CALL_DISCONNECTED_BEFORE_BEING_ADDED_ERROR_UUID,
-                        EMERGENCY_CALL_DISCONNECTED_BEFORE_BEING_ADDED_ERROR_MSG);
-            }
             mListeners.forEach(l -> l.onCreateConnectionFailed(call));
         }
 
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 0c403b4..a43000a 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -2396,6 +2396,7 @@
         BindCallback callback = new BindCallback() {
             @Override
             public void onSuccess() {
+                if (!isServiceValid("connectionServiceFocusLost")) return;
                 try {
                     mServiceInterface.connectionServiceFocusLost(
                             Log.getExternalSession(TELECOM_ABBREVIATION));
@@ -2415,6 +2416,7 @@
         BindCallback callback = new BindCallback() {
             @Override
             public void onSuccess() {
+                if (!isServiceValid("connectionServiceFocusGained")) return;
                 try {
                     mServiceInterface.connectionServiceFocusGained(
                             Log.getExternalSession(TELECOM_ABBREVIATION));
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 166ebd9..ca264c1 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -2430,7 +2430,9 @@
                 try {
                     inCallService.updateCall(
                             sanitizeParcelableCallForService(info, parcelableCall));
-                } catch (RemoteException ignored) {
+                } catch (RemoteException exception) {
+                    Log.w(this, "Call status update did not send to: "
+                                + componentName +" successfully with error " + exception);
                 }
             }
             Log.i(this, "Components updated: %s", componentsUpdated);
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index a01c35b..1dd68c9 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -1553,6 +1553,23 @@
                             }
                             mCallIntentProcessorAdapter.processIncomingCallIntent(
                                     mCallsManager, intent);
+                            if (mFeatureFlags.earlyBindingToIncallService()) {
+                                PhoneAccount account =
+                                        mPhoneAccountRegistrar.getPhoneAccountUnchecked(
+                                                phoneAccountHandle);
+                                Bundle accountExtra =
+                                        account == null ? new Bundle() : account.getExtras();
+                                PackageManager packageManager = mContext.getPackageManager();
+                                // Start binding to InCallServices for wearable calls that do not
+                                // require call filtering. This is to wake up default dialer earlier
+                                // to mitigate InCallService binding latency.
+                                if (packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)
+                                        && accountExtra != null && accountExtra.getBoolean(
+                                        PhoneAccount.EXTRA_SKIP_CALL_FILTERING,
+                                        false)) {
+                                    mCallsManager.getInCallController().bindToServices(null);
+                                }
+                            }
                         } finally {
                             Binder.restoreCallingIdentity(token);
                         }
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
index ce07532..b411b25 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
@@ -20,8 +20,8 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHearingAid;
-import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothLeAudio;
+import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.media.AudioDeviceInfo;
 import android.os.Message;
@@ -38,12 +38,10 @@
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.Timeouts;
 
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
@@ -138,7 +136,7 @@
                 Log.w(LOG_TAG, "Entering AudioOff state but device %s appears to be connected. " +
                         "Switching to audio-on state for that device.", erroneouslyConnectedDevice);
                 // change this to just transition to the new audio on state
-                transitionToActualState();
+                transitionToActualState(null /* excludeAddress */);
             }
             cleanupStatesForDisconnectedDevices();
             if (mListener != null) {
@@ -261,7 +259,7 @@
                     case LOST_DEVICE:
                         removeDevice((String) args.arg2);
                         if (Objects.equals(address, mDeviceAddress)) {
-                            transitionToActualState();
+                            transitionToActualState(null /* excludeAddress */);
                         }
                         break;
                     case CONNECT_BT:
@@ -301,7 +299,7 @@
                     case CONNECTION_TIMEOUT:
                         Log.i(LOG_TAG, "Connection with device %s timed out.",
                                 mDeviceAddress);
-                        transitionToActualState();
+                        transitionToActualState(null /* excludeAddress */);
                         break;
                     case BT_AUDIO_IS_ON:
                         if (Objects.equals(mDeviceAddress, address)) {
@@ -318,7 +316,7 @@
                         if (Objects.equals(mDeviceAddress, address) || address == null) {
                             Log.i(LOG_TAG, "Connection with device %s failed.",
                                     mDeviceAddress);
-                            transitionToActualState();
+                            transitionToActualState(address);
                         } else {
                             Log.w(LOG_TAG, "Got BT lost message for device %s while" +
                                     " connecting to %s.", address, mDeviceAddress);
@@ -378,7 +376,7 @@
                     case LOST_DEVICE:
                         removeDevice((String) args.arg2);
                         if (Objects.equals(address, mDeviceAddress)) {
-                            transitionToActualState();
+                            transitionToActualState(null /* excludeAddress */);
                         }
                         break;
                     case CONNECT_BT:
@@ -435,7 +433,7 @@
                     case BT_AUDIO_LOST:
                         if (Objects.equals(mDeviceAddress, address) || address == null) {
                             Log.i(LOG_TAG, "BT connection with device %s lost.", mDeviceAddress);
-                            transitionToActualState();
+                            transitionToActualState(address);
                         } else {
                             Log.w(LOG_TAG, "Got BT lost message for device %s while" +
                                     " connected to %s.", address, mDeviceAddress);
@@ -721,7 +719,7 @@
                 actualAddress)) {
             Log.i(this, "trying to connect to already connected device -- skipping connection"
                     + " and going into the actual connected state.");
-            transitionToActualState();
+            transitionToActualState(null /* excludeAddress */);
             return null;
         }
 
@@ -757,9 +755,10 @@
         return null;
     }
 
-    private void transitionToActualState() {
+    private void transitionToActualState(String excludeAddress) {
         BluetoothDevice possiblyAlreadyConnectedDevice = getBluetoothAudioConnectedDevice();
-        if (possiblyAlreadyConnectedDevice != null) {
+        if (possiblyAlreadyConnectedDevice != null
+                && !possiblyAlreadyConnectedDevice.getAddress().equals(excludeAddress)) {
             Log.i(LOG_TAG, "Device %s is already connected; going to AudioConnected.",
                     possiblyAlreadyConnectedDevice);
             transitionTo(getConnectedStateForAddress(
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
index 2b5e5ac..8e31f9c 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
@@ -16,6 +16,15 @@
 
 package com.android.server.telecom.tests;
 
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
@@ -47,16 +56,6 @@
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 @RunWith(JUnit4.class)
 public class BluetoothRouteManagerTest extends TelecomTestCase {
     private static final int TEST_TIMEOUT = 1000;
@@ -173,6 +172,19 @@
         sm.quitNow();
     }
 
+    @SmallTest
+    @Test
+    public void testSkipInactiveBtDeviceWhenEvaluateActualState() {
+        BluetoothRouteManager sm = setupStateMachine(
+                BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, HEARING_AID_DEVICE);
+        setupConnectedDevices(null, new BluetoothDevice[]{HEARING_AID_DEVICE},
+                null, null, HEARING_AID_DEVICE, null);
+        executeRoutingAction(sm, BluetoothRouteManager.BT_AUDIO_LOST,
+                HEARING_AID_DEVICE.getAddress());
+        assertEquals(BluetoothRouteManager.AUDIO_OFF_STATE_NAME, sm.getCurrentState().getName());
+        sm.quitNow();
+    }
+
     private BluetoothRouteManager setupStateMachine(String initialState,
             BluetoothDevice initialDevice) {
         resetMocks();
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 873f9ed..8460c18 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -2508,6 +2508,30 @@
         assertEquals(DEFAULT_CALL_SCREENING_APP, outgoingCall.getPostCallPackageName());
     }
 
+    /**
+     * Verify the only call state set from calling onSuccessfulOutgoingCall is CallState.DIALING.
+     */
+    @SmallTest
+    @Test
+    public void testOutgoingCallStateIsSetToAPreviousStateAndIgnored() {
+        Call outgoingCall = addSpyCall(CallState.CONNECTING);
+        mCallsManager.onSuccessfulOutgoingCall(outgoingCall, CallState.NEW);
+        verify(outgoingCall, never()).setState(eq(CallState.NEW), any());
+        verify(outgoingCall, times(1)).setState(eq(CallState.DIALING), any());
+    }
+
+    /**
+     * Verify a ConnectionService can start the call in the active state and avoid the dialing state
+     */
+    @SmallTest
+    @Test
+    public void testOutgoingCallStateCanAvoidDialingAndGoStraightToActive() {
+        Call outgoingCall = addSpyCall(CallState.CONNECTING);
+        mCallsManager.onSuccessfulOutgoingCall(outgoingCall, CallState.ACTIVE);
+        verify(outgoingCall, never()).setState(eq(CallState.DIALING), any());
+        verify(outgoingCall, times(1)).setState(eq(CallState.ACTIVE), any());
+    }
+
     @SmallTest
     @Test
     public void testRejectIncomingCallOnPAHInactive_SecondaryUser() throws Exception {
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index 6d50e35..e9466ee 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -58,6 +58,7 @@
 import com.android.server.telecom.CallState;
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.DefaultDialerCache;
+import com.android.server.telecom.InCallController;
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.TelecomServiceImpl;
 import com.android.server.telecom.TelecomSystem;
@@ -197,6 +198,8 @@
     @Mock private AnomalyReporterAdapter mAnomalyReporterAdapter;
     @Mock private FeatureFlags mFeatureFlags;
 
+    @Mock private InCallController mInCallController;
+
     private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
 
     private static final String DEFAULT_DIALER_PACKAGE = "com.google.android.dialer";
@@ -223,6 +226,7 @@
 
         doReturn(mContext).when(mContext).getApplicationContext();
         doReturn(mContext).when(mContext).createContextAsUser(any(UserHandle.class), anyInt());
+        when(mFakeCallsManager.getInCallController()).thenReturn(mInCallController);
         doNothing().when(mContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class),
                 anyString());
         when(mContext.checkCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS))
@@ -265,6 +269,7 @@
 
         mPackageManager = mContext.getPackageManager();
         when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(Binder.getCallingUid());
+        when(mFeatureFlags.earlyBindingToIncallService()).thenReturn(true);
     }
 
     @Override
@@ -1045,6 +1050,7 @@
 
         verify(mFakePhoneAccountRegistrar).getPhoneAccount(
                 TEL_PA_HANDLE_16, TEL_PA_HANDLE_16.getUserHandle());
+        verify(mInCallController, never()).bindToServices(any());
         addCallTestHelper(TelecomManager.ACTION_INCOMING_CALL,
                 CallIntentProcessor.KEY_IS_INCOMING_CALL, extras,
                 TEL_PA_HANDLE_16, false);
@@ -1052,6 +1058,81 @@
 
     @SmallTest
     @Test
+    public void testAddNewIncomingFlagDisabledNoEarlyBinding() throws Exception {
+        when(mFeatureFlags.earlyBindingToIncallService()).thenReturn(false);
+        PhoneAccount phoneAccount = makeSkipCallFilteringPhoneAccount(TEL_PA_HANDLE_16).build();
+        phoneAccount.setIsEnabled(true);
+        doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccount(
+                eq(TEL_PA_HANDLE_16), any(UserHandle.class));
+        doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccountUnchecked(
+                eq(TEL_PA_HANDLE_16));
+        doNothing().when(mAppOpsManager).checkPackage(anyInt(), anyString());
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)).thenReturn(true);
+        Bundle extras = createSampleExtras();
+
+        mTSIBinder.addNewIncomingCall(TEL_PA_HANDLE_16, extras, CALLING_PACKAGE);
+
+        verify(mInCallController, never()).bindToServices(null);
+    }
+
+    @SmallTest
+    @Test
+    public void testAddNewIncomingCallEarlyBindingForNoCallFilterCalls() throws Exception {
+        PhoneAccount phoneAccount = makeSkipCallFilteringPhoneAccount(TEL_PA_HANDLE_16).build();
+        phoneAccount.setIsEnabled(true);
+        doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccount(
+                eq(TEL_PA_HANDLE_16), any(UserHandle.class));
+        doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccountUnchecked(
+                eq(TEL_PA_HANDLE_16));
+        doNothing().when(mAppOpsManager).checkPackage(anyInt(), anyString());
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)).thenReturn(true);
+        Bundle extras = createSampleExtras();
+
+        mTSIBinder.addNewIncomingCall(TEL_PA_HANDLE_16, extras, CALLING_PACKAGE);
+
+        verify(mInCallController).bindToServices(null);
+    }
+
+    @SmallTest
+    @Test
+    public void testAddNewIncomingCallEarlyBindingNotEnableForNonWatchDevices() throws Exception {
+        PhoneAccount phoneAccount = makeSkipCallFilteringPhoneAccount(TEL_PA_HANDLE_16).build();
+        phoneAccount.setIsEnabled(true);
+        doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccount(
+                eq(TEL_PA_HANDLE_16), any(UserHandle.class));
+        doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccountUnchecked(
+                eq(TEL_PA_HANDLE_16));
+        doNothing().when(mAppOpsManager).checkPackage(anyInt(), anyString());
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)).thenReturn(false);
+        Bundle extras = createSampleExtras();
+
+        mTSIBinder.addNewIncomingCall(TEL_PA_HANDLE_16, extras, CALLING_PACKAGE);
+
+        verify(mInCallController, never()).bindToServices(null);
+    }
+
+    @SmallTest
+    @Test
+    public void testAddNewIncomingCallEarlyBindingNotEnableForPhoneAccountHasCallFilters()
+            throws Exception {
+        PhoneAccount phoneAccount = makePhoneAccount(TEL_PA_HANDLE_16).build();
+        phoneAccount.setIsEnabled(true);
+        doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccount(
+                eq(TEL_PA_HANDLE_16), any(UserHandle.class));
+        doReturn(phoneAccount).when(mFakePhoneAccountRegistrar).getPhoneAccountUnchecked(
+                eq(TEL_PA_HANDLE_16));
+        doNothing().when(mAppOpsManager).checkPackage(anyInt(), anyString());
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)).thenReturn(true);
+        Bundle extras = createSampleExtras();
+
+        mTSIBinder.addNewIncomingCall(TEL_PA_HANDLE_16, extras, CALLING_PACKAGE);
+
+        verify(mInCallController, never()).bindToServices(null);
+    }
+
+
+    @SmallTest
+    @Test
     public void testAddNewIncomingCallFailure() throws Exception {
         try {
             mTSIBinder.addNewIncomingCall(TEL_PA_HANDLE_16, null, CALLING_PACKAGE);
@@ -2171,6 +2252,12 @@
         return new PhoneAccount.Builder(paHandle, "testLabel");
     }
 
+    private PhoneAccount.Builder makeSkipCallFilteringPhoneAccount(PhoneAccountHandle paHandle) {
+        Bundle extras = new Bundle();
+        extras.putBoolean(PhoneAccount.EXTRA_SKIP_CALL_FILTERING, true);
+        return new PhoneAccount.Builder(paHandle, "testLabel").setExtras(extras);
+    }
+
     private Bundle createSampleExtras() {
         Bundle extras = new Bundle();
         extras.putString("test_key", "test_value");