Merge "Fix issue where conference gets logged when no CEP is enabled."
diff --git a/proto/telecom.proto b/proto/telecom.proto
index 2f4fae8..73eba87 100644
--- a/proto/telecom.proto
+++ b/proto/telecom.proto
@@ -16,6 +16,9 @@
 
   // Hardware revision (EVT, DVT, PVT etc.)
   optional string hardware_revision = 3;
+
+  // Carrier ID that the device is associated to
+  optional int32 carrier_id = 4;
 }
 
 message LogSessionTiming {
diff --git a/src/com/android/server/telecom/Analytics.java b/src/com/android/server/telecom/Analytics.java
index 1d3a90e..2997454 100644
--- a/src/com/android/server/telecom/Analytics.java
+++ b/src/com/android/server/telecom/Analytics.java
@@ -16,6 +16,7 @@
 
 package com.android.server.telecom;
 
+import android.content.Context;
 import android.os.SystemProperties;
 
 import android.telecom.Connection;
@@ -23,6 +24,8 @@
 import android.telecom.Logging.EventManager;
 import android.telecom.ParcelableCallAnalytics;
 import android.telecom.TelecomAnalytics;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.util.Base64;
 import android.telecom.Log;
 
@@ -37,10 +40,12 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.PriorityQueue;
 import java.util.concurrent.LinkedBlockingDeque;
 import java.util.stream.Collectors;
@@ -628,7 +633,7 @@
         return new TelecomAnalytics(sessionTimings, calls);
     }
 
-    public static void dumpToEncodedProto(PrintWriter pw, String[] args) {
+    public static void dumpToEncodedProto(Context context, PrintWriter pw, String[] args) {
         TelecomLogClass.TelecomLog result = new TelecomLogClass.TelecomLog();
 
         synchronized (sLock) {
@@ -642,6 +647,7 @@
                             .setTimeMillis(timing.getTime()))
                     .toArray(TelecomLogClass.LogSessionTiming[]::new);
             result.setHardwareRevision(SystemProperties.get("ro.boot.revision", ""));
+            result.setCarrierId(getCarrierId(context));
             if (args.length > 1 && CLEAR_ANALYTICS_ARG.equals(args[1])) {
                 sCallIdToInfo.clear();
                 sSessionTimings.clear();
@@ -652,6 +658,29 @@
         pw.write(encodedProto);
     }
 
+    private static int getCarrierId(Context context) {
+        SubscriptionManager subscriptionManager =
+                context.getSystemService(SubscriptionManager.class);
+        List<SubscriptionInfo> subInfos = subscriptionManager.getActiveSubscriptionInfoList();
+        if (subInfos == null) {
+            return -1;
+        }
+        return subInfos.stream()
+                .max(Comparator.comparing(Analytics::scoreSubscriptionInfo))
+                .map(SubscriptionInfo::getCarrierId).orElse(-1);
+    }
+
+    // Copied over from Telephony's server-side logic for consistency
+    private static int scoreSubscriptionInfo(SubscriptionInfo subInfo) {
+        final int scoreCarrierId = 0b100;
+        final int scoreNotOpportunistic = 0b010;
+        final int scoreSlot0 = 0b001;
+
+        return ((subInfo.getCarrierId() >= 0) ? scoreCarrierId : 0)
+                + (subInfo.isOpportunistic() ? 0 : scoreNotOpportunistic)
+                + ((subInfo.getSimSlotIndex() == 0) ? scoreSlot0 : 0);
+    }
+
     public static void dump(IndentingPrintWriter writer) {
         synchronized (sLock) {
             int prefixLength = CallsManager.TELECOM_CALL_ID_PREFIX.length();
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index ecc6359..b507758 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -47,6 +47,8 @@
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
 import android.text.TextUtils;
 import android.util.StatsLog;
 import android.os.UserHandle;
@@ -62,11 +64,13 @@
 import java.lang.String;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -336,6 +340,10 @@
 
     private boolean mIsEmergencyCall;
 
+    // The Call is considered an emergency call for testing, but will not actually connect to
+    // emergency services.
+    private boolean mIsTestEmergencyCall;
+
     private boolean mSpeakerphoneOn;
 
     private boolean mIsDisconnectingChildCall = false;
@@ -431,6 +439,7 @@
 
     private boolean mWasConferencePreviouslyMerged = false;
     private boolean mWasHighDefAudio = false;
+    private boolean mWasWifi = false;
 
     // For conferences which support merge/swap at their level, we retain a notion of an active
     // call. This is used for BluetoothPhoneService.  In order to support hold/merge, it must have
@@ -1072,10 +1081,13 @@
             // call, it will remain so for the rest of it's lifetime.
             if (!mIsEmergencyCall) {
                 mIsEmergencyCall = mHandle != null &&
-                        mPhoneNumberUtilsAdapter.isLocalEmergencyNumber(mContext,
-                                mHandle.getSchemeSpecificPart());
+                        getTelephonyManager().isEmergencyNumber(mHandle.getSchemeSpecificPart());
                 mAnalytics.setCallIsEmergency(mIsEmergencyCall);
             }
+            if (!mIsTestEmergencyCall) {
+                mIsTestEmergencyCall = mHandle != null &&
+                        isTestEmergencyCall(mHandle.getSchemeSpecificPart());
+            }
             startCallerInfoLookup();
             for (Listener l : mListeners) {
                 l.onHandleChanged(this);
@@ -1083,6 +1095,14 @@
         }
     }
 
+    private boolean isTestEmergencyCall(String number) {
+        Map<Integer, List<EmergencyNumber>> eMap = getTelephonyManager().getEmergencyNumberList();
+        return eMap.values().stream().flatMap(Collection::stream)
+                .anyMatch(eNumber ->
+                        eNumber.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST) &&
+                                number.equals(eNumber.getNumber()));
+    }
+
     public String getCallerDisplayName() {
         return mCallerDisplayName;
     }
@@ -1143,6 +1163,15 @@
     }
 
     /**
+     * @return {@code true} if this an outgoing call to a test emergency number (and NOT to
+     * emergency services). Used for testing purposes to differentiate between a real and fake
+     * emergency call for safety reasons during testing.
+     */
+    public boolean isTestEmergencyCall() {
+        return mIsTestEmergencyCall;
+    }
+
+    /**
      * @return {@code true} if the network has identified this call as an emergency call.
      */
     public boolean isNetworkIdentifiedEmergencyCall() {
@@ -1565,12 +1594,12 @@
                         getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
                 mWasEverRtt = true;
                 if (isEmergencyCall()) {
-                    mCallsManager.setAudioRoute(CallAudioState.ROUTE_SPEAKER, null);
                     mCallsManager.mute(false);
                 }
             }
             mWasHighDefAudio = (connectionProperties & Connection.PROPERTY_HIGH_DEF_AUDIO) ==
                     Connection.PROPERTY_HIGH_DEF_AUDIO;
+            mWasWifi = (connectionProperties & Connection.PROPERTY_WIFI) > 0;
             for (Listener l : mListeners) {
                 l.onConnectionPropertiesChanged(this, didRttChange);
             }
@@ -3162,6 +3191,10 @@
         }
     }
 
+    private TelephonyManager getTelephonyManager() {
+        return mContext.getSystemService(TelephonyManager.class);
+    }
+
     /**
      * Sets whether this {@link Call} is a conference or not.
      * @param isConference
@@ -3201,6 +3234,15 @@
         return mWasHighDefAudio;
     }
 
+    /**
+     * Returns wether or not Wifi call was used.
+     *
+     * @return true if wifi call was used during this call.
+     */
+    boolean wasWifi() {
+        return mWasWifi;
+    }
+
     public void setIsUsingCallFiltering(boolean isUsingCallFiltering) {
         mIsUsingCallFiltering = isUsingCallFiltering;
     }
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 7e4c3ba..c01c9aa 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -26,6 +26,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.telecom.CallAudioModeStateMachine.MessageArgs.Builder;
 import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
 
 import java.util.Collection;
@@ -44,6 +45,7 @@
     private final LinkedHashSet<Call> mActiveDialingOrConnectingCalls;
     private final LinkedHashSet<Call> mRingingCalls;
     private final LinkedHashSet<Call> mHoldingCalls;
+    private final LinkedHashSet<Call> mAudioProcessingCalls;
     private final Set<Call> mCalls;
     private final SparseArray<LinkedHashSet<Call>> mCallStateToCalls;
 
@@ -69,9 +71,10 @@
             RingbackPlayer ringbackPlayer,
             BluetoothStateReceiver bluetoothStateReceiver,
             DtmfLocalTonePlayer dtmfLocalTonePlayer) {
-        mActiveDialingOrConnectingCalls = new LinkedHashSet<>();
-        mRingingCalls = new LinkedHashSet<>();
-        mHoldingCalls = new LinkedHashSet<>();
+        mActiveDialingOrConnectingCalls = new LinkedHashSet<>(1);
+        mRingingCalls = new LinkedHashSet<>(1);
+        mHoldingCalls = new LinkedHashSet<>(1);
+        mAudioProcessingCalls = new LinkedHashSet<>(1);
         mCalls = new HashSet<>();
         mCallStateToCalls = new SparseArray<LinkedHashSet<Call>>() {{
             put(CallState.CONNECTING, mActiveDialingOrConnectingCalls);
@@ -80,6 +83,8 @@
             put(CallState.PULLING, mActiveDialingOrConnectingCalls);
             put(CallState.RINGING, mRingingCalls);
             put(CallState.ON_HOLD, mHoldingCalls);
+            put(CallState.SIMULATED_RINGING, mRingingCalls);
+            put(CallState.AUDIO_PROCESSING, mAudioProcessingCalls);
         }};
 
         mCallAudioRouteStateMachine = callAudioRouteStateMachine;
@@ -511,6 +516,13 @@
         pw.increaseIndent();
         mCallAudioRouteStateMachine.dumpPendingMessages(pw);
         pw.decreaseIndent();
+
+        pw.println("BluetoothDeviceManager:");
+        pw.increaseIndent();
+        if (mBluetoothStateReceiver.getBluetoothDeviceManager() != null) {
+            mBluetoothStateReceiver.getBluetoothDeviceManager().dump(pw);
+        }
+        pw.decreaseIndent();
     }
 
     @VisibleForTesting
@@ -534,6 +546,7 @@
                 onCallLeavingActiveDialingOrConnecting();
                 break;
             case CallState.RINGING:
+            case CallState.SIMULATED_RINGING:
             case CallState.ANSWERED:
                 onCallLeavingRinging();
                 break;
@@ -547,6 +560,9 @@
                 stopRingbackForCall(call);
                 onCallLeavingActiveDialingOrConnecting();
                 break;
+            case CallState.AUDIO_PROCESSING:
+                onCallLeavingAudioProcessing();
+                break;
         }
     }
 
@@ -557,6 +573,7 @@
                 onCallEnteringActiveDialingOrConnecting();
                 break;
             case CallState.RINGING:
+            case CallState.SIMULATED_RINGING:
                 onCallEnteringRinging();
                 break;
             case CallState.ON_HOLD:
@@ -574,6 +591,25 @@
                     onCallEnteringActiveDialingOrConnecting();
                 }
                 break;
+            case CallState.AUDIO_PROCESSING:
+                onCallEnteringAudioProcessing();
+                break;
+        }
+    }
+
+    private void onCallLeavingAudioProcessing() {
+        if (mAudioProcessingCalls.size() == 0) {
+            mCallAudioModeStateMachine.sendMessageWithArgs(
+                    CallAudioModeStateMachine.NO_MORE_AUDIO_PROCESSING_CALLS,
+                    makeArgsForModeStateMachine());
+        }
+    }
+
+    private void onCallEnteringAudioProcessing() {
+        if (mAudioProcessingCalls.size() == 1) {
+            mCallAudioModeStateMachine.sendMessageWithArgs(
+                    CallAudioModeStateMachine.NEW_AUDIO_PROCESSING_CALL,
+                    makeArgsForModeStateMachine());
         }
     }
 
@@ -655,13 +691,15 @@
 
     @NonNull
     private CallAudioModeStateMachine.MessageArgs makeArgsForModeStateMachine() {
-        return new CallAudioModeStateMachine.MessageArgs(
-                mActiveDialingOrConnectingCalls.size() > 0,
-                mRingingCalls.size() > 0,
-                mHoldingCalls.size() > 0,
-                mIsTonePlaying,
-                mForegroundCall != null && mForegroundCall.getIsVoipAudioMode(),
-                Log.createSubsession());
+        return new Builder()
+                .setHasActiveOrDialingCalls(mActiveDialingOrConnectingCalls.size() > 0)
+                .setHasRingingCalls(mRingingCalls.size() > 0)
+                .setHasHoldingCalls(mHoldingCalls.size() > 0)
+                .setHasAudioProcessingCalls(mAudioProcessingCalls.size() > 0)
+                .setIsTonePlaying(mIsTonePlaying)
+                .setForegroundCallIsVoip(
+                        mForegroundCall != null && mForegroundCall.getIsVoipAudioMode())
+                .setSession(Log.createSubsession()).build();
     }
 
     private HashSet<Call> getBinForCall(Call call) {
diff --git a/src/com/android/server/telecom/CallAudioModeStateMachine.java b/src/com/android/server/telecom/CallAudioModeStateMachine.java
index 42a1d36..06be216 100644
--- a/src/com/android/server/telecom/CallAudioModeStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioModeStateMachine.java
@@ -17,6 +17,7 @@
 package com.android.server.telecom;
 
 import android.media.AudioManager;
+import android.os.Looper;
 import android.os.Message;
 import android.telecom.Log;
 import android.telecom.Logging.Runnable;
@@ -40,53 +41,108 @@
         public boolean hasActiveOrDialingCalls;
         public boolean hasRingingCalls;
         public boolean hasHoldingCalls;
+        public boolean hasAudioProcessingCalls;
         public boolean isTonePlaying;
         public boolean foregroundCallIsVoip;
         public Session session;
 
-        public MessageArgs(boolean hasActiveOrDialingCalls, boolean hasRingingCalls,
-                boolean hasHoldingCalls, boolean isTonePlaying, boolean foregroundCallIsVoip,
-                Session session) {
+        private MessageArgs(boolean hasActiveOrDialingCalls, boolean hasRingingCalls,
+                boolean hasHoldingCalls, boolean hasAudioProcessingCalls, boolean isTonePlaying,
+                boolean foregroundCallIsVoip, Session session) {
             this.hasActiveOrDialingCalls = hasActiveOrDialingCalls;
             this.hasRingingCalls = hasRingingCalls;
             this.hasHoldingCalls = hasHoldingCalls;
+            this.hasAudioProcessingCalls = hasAudioProcessingCalls;
             this.isTonePlaying = isTonePlaying;
             this.foregroundCallIsVoip = foregroundCallIsVoip;
             this.session = session;
         }
 
-        public MessageArgs() {
-            this.session = Log.createSubsession();
-        }
-
         @Override
         public String toString() {
             return "MessageArgs{" +
                     "hasActiveCalls=" + hasActiveOrDialingCalls +
                     ", hasRingingCalls=" + hasRingingCalls +
                     ", hasHoldingCalls=" + hasHoldingCalls +
+                    ", hasAudioProcessingCalls=" + hasAudioProcessingCalls +
                     ", isTonePlaying=" + isTonePlaying +
                     ", foregroundCallIsVoip=" + foregroundCallIsVoip +
                     ", session=" + session +
                     '}';
         }
+
+        public static class Builder {
+            private boolean mHasActiveOrDialingCalls;
+            private boolean mHasRingingCalls;
+            private boolean mHasHoldingCalls;
+            private boolean mHasAudioProcessingCalls;
+            private boolean mIsTonePlaying;
+            private boolean mForegroundCallIsVoip;
+            private Session mSession;
+
+            public Builder setHasActiveOrDialingCalls(boolean hasActiveOrDialingCalls) {
+                mHasActiveOrDialingCalls = hasActiveOrDialingCalls;
+                return this;
+            }
+
+            public Builder setHasRingingCalls(boolean hasRingingCalls) {
+                mHasRingingCalls = hasRingingCalls;
+                return this;
+            }
+
+            public Builder setHasHoldingCalls(boolean hasHoldingCalls) {
+                mHasHoldingCalls = hasHoldingCalls;
+                return this;
+            }
+
+            public Builder setHasAudioProcessingCalls(boolean hasAudioProcessingCalls) {
+                mHasAudioProcessingCalls = hasAudioProcessingCalls;
+                return this;
+            }
+
+            public Builder setIsTonePlaying(boolean isTonePlaying) {
+                mIsTonePlaying = isTonePlaying;
+                return this;
+            }
+
+            public Builder setForegroundCallIsVoip(boolean foregroundCallIsVoip) {
+                mForegroundCallIsVoip = foregroundCallIsVoip;
+                return this;
+            }
+
+            public Builder setSession(Session session) {
+                mSession = session;
+                return this;
+            }
+
+            public MessageArgs build() {
+                return new MessageArgs(mHasActiveOrDialingCalls, mHasRingingCalls, mHasHoldingCalls,
+                        mHasAudioProcessingCalls, mIsTonePlaying, mForegroundCallIsVoip, mSession);
+            }
+        }
     }
 
+    // TODO: remove this and replace when the new audio mode gets checked in.
+    public static final int NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING = AudioManager.MODE_NORMAL;
+
     public static final int INITIALIZE = 1;
     // These ENTER_*_FOCUS commands are for testing.
     public static final int ENTER_CALL_FOCUS_FOR_TESTING = 2;
     public static final int ENTER_COMMS_FOCUS_FOR_TESTING = 3;
     public static final int ENTER_RING_FOCUS_FOR_TESTING = 4;
     public static final int ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING = 5;
-    public static final int ABANDON_FOCUS_FOR_TESTING = 6;
+    public static final int ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING = 6;
+    public static final int ABANDON_FOCUS_FOR_TESTING = 7;
 
     public static final int NO_MORE_ACTIVE_OR_DIALING_CALLS = 1001;
     public static final int NO_MORE_RINGING_CALLS = 1002;
     public static final int NO_MORE_HOLDING_CALLS = 1003;
+    public static final int NO_MORE_AUDIO_PROCESSING_CALLS = 1004;
 
     public static final int NEW_ACTIVE_OR_DIALING_CALL = 2001;
     public static final int NEW_RINGING_CALL = 2002;
     public static final int NEW_HOLDING_CALL = 2003;
+    public static final int NEW_AUDIO_PROCESSING_CALL = 2004;
 
     public static final int TONE_STARTED_PLAYING = 3001;
     public static final int TONE_STOPPED_PLAYING = 3002;
@@ -101,14 +157,17 @@
         put(ENTER_CALL_FOCUS_FOR_TESTING, "ENTER_CALL_FOCUS_FOR_TESTING");
         put(ENTER_COMMS_FOCUS_FOR_TESTING, "ENTER_COMMS_FOCUS_FOR_TESTING");
         put(ENTER_RING_FOCUS_FOR_TESTING, "ENTER_RING_FOCUS_FOR_TESTING");
+        put(ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING, "ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING");
         put(ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, "ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING");
         put(ABANDON_FOCUS_FOR_TESTING, "ABANDON_FOCUS_FOR_TESTING");
         put(NO_MORE_ACTIVE_OR_DIALING_CALLS, "NO_MORE_ACTIVE_OR_DIALING_CALLS");
         put(NO_MORE_RINGING_CALLS, "NO_MORE_RINGING_CALLS");
         put(NO_MORE_HOLDING_CALLS, "NO_MORE_HOLDING_CALLS");
+        put(NO_MORE_AUDIO_PROCESSING_CALLS, "NO_MORE_AUDIO_PROCESSING_CALLS");
         put(NEW_ACTIVE_OR_DIALING_CALL, "NEW_ACTIVE_OR_DIALING_CALL");
         put(NEW_RINGING_CALL, "NEW_RINGING_CALL");
         put(NEW_HOLDING_CALL, "NEW_HOLDING_CALL");
+        put(NEW_AUDIO_PROCESSING_CALL, "NEW_AUDIO_PROCESSING_CALL");
         put(TONE_STARTED_PLAYING, "TONE_STARTED_PLAYING");
         put(TONE_STOPPED_PLAYING, "TONE_STOPPED_PLAYING");
         put(FOREGROUND_VOIP_MODE_CHANGE, "FOREGROUND_VOIP_MODE_CHANGE");
@@ -119,6 +178,8 @@
 
     public static final String TONE_HOLD_STATE_NAME = OtherFocusState.class.getSimpleName();
     public static final String UNFOCUSED_STATE_NAME = UnfocusedState.class.getSimpleName();
+    public static final String AUDIO_PROCESSING_STATE_NAME =
+            AudioProcessingFocusState.class.getSimpleName();
     public static final String CALL_STATE_NAME = SimCallFocusState.class.getSimpleName();
     public static final String RING_STATE_NAME = RingingFocusState.class.getSimpleName();
     public static final String COMMS_STATE_NAME = VoipCallFocusState.class.getSimpleName();
@@ -139,6 +200,9 @@
                 case ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING:
                     transitionTo(mOtherFocusState);
                     return HANDLED;
+                case ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING:
+                    transitionTo(mAudioProcessingFocusState);
+                    return HANDLED;
                 case ABANDON_FOCUS_FOR_TESTING:
                     transitionTo(mUnfocusedState);
                     return HANDLED;
@@ -160,8 +224,8 @@
         public void enter() {
             if (mIsInitialized) {
                 Log.i(LOG_TAG, "Abandoning audio focus: now UNFOCUSED");
-                mAudioManager.abandonAudioFocusForCall();
                 mAudioManager.setMode(AudioManager.MODE_NORMAL);
+                mAudioManager.abandonAudioFocusForCall();
 
                 mMostRecentMode = AudioManager.MODE_NORMAL;
                 mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.NO_FOCUS);
@@ -184,6 +248,73 @@
                 case NO_MORE_HOLDING_CALLS:
                     // Do nothing.
                     return HANDLED;
+                case NO_MORE_AUDIO_PROCESSING_CALLS:
+                    // Do nothing.
+                    return HANDLED;
+                case NEW_ACTIVE_OR_DIALING_CALL:
+                    transitionTo(args.foregroundCallIsVoip
+                            ? mVoipCallFocusState : mSimCallFocusState);
+                    return HANDLED;
+                case NEW_RINGING_CALL:
+                    transitionTo(mRingingFocusState);
+                    return HANDLED;
+                case NEW_AUDIO_PROCESSING_CALL:
+                    transitionTo(mAudioProcessingFocusState);
+                    return HANDLED;
+                case NEW_HOLDING_CALL:
+                    // This really shouldn't happen, but transition to the focused state anyway.
+                    Log.w(LOG_TAG, "Call was surprisingly put into hold from an unknown state." +
+                            " Args are: \n" + args.toString());
+                    transitionTo(mOtherFocusState);
+                    return HANDLED;
+                case TONE_STARTED_PLAYING:
+                    // This shouldn't happen either, but perform the action anyway.
+                    Log.w(LOG_TAG, "Tone started playing unexpectedly. Args are: \n"
+                            + args.toString());
+                    return HANDLED;
+                default:
+                    // The forced focus switch commands are handled by BaseState.
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    private class AudioProcessingFocusState extends BaseState {
+        @Override
+        public void enter() {
+            if (mIsInitialized) {
+                Log.i(LOG_TAG, "Abandoning audio focus: now audio processing");
+                mAudioManager.setMode(NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING);
+                mAudioManager.abandonAudioFocusForCall();
+
+                mMostRecentMode = NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING;
+                mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.NO_FOCUS);
+            }
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            if (super.processMessage(msg) == HANDLED) {
+                return HANDLED;
+            }
+            MessageArgs args = (MessageArgs) msg.obj;
+            switch (msg.what) {
+                case NO_MORE_ACTIVE_OR_DIALING_CALLS:
+                    // Do nothing.
+                    return HANDLED;
+                case NO_MORE_RINGING_CALLS:
+                    // Do nothing.
+                    return HANDLED;
+                case NO_MORE_HOLDING_CALLS:
+                    // Do nothing.
+                    return HANDLED;
+                case NO_MORE_AUDIO_PROCESSING_CALLS:
+                    BaseState destState = calculateProperStateFromArgs(args);
+                    if (destState == this) {
+                        Log.w(LOG_TAG, "Got spurious NO_MORE_AUDIO_PROCESSING_CALLS");
+                    }
+                    transitionTo(destState);
+                    return HANDLED;
                 case NEW_ACTIVE_OR_DIALING_CALL:
                     transitionTo(args.foregroundCallIsVoip
                             ? mVoipCallFocusState : mSimCallFocusState);
@@ -192,11 +323,15 @@
                     transitionTo(mRingingFocusState);
                     return HANDLED;
                 case NEW_HOLDING_CALL:
-                    // This really shouldn't happen, but transition to the focused state anyway.
+                    // This really shouldn't happen, but recalculate from args and do the transition
                     Log.w(LOG_TAG, "Call was surprisingly put into hold from an unknown state." +
                             " Args are: \n" + args.toString());
                     transitionTo(mOtherFocusState);
                     return HANDLED;
+                case NEW_AUDIO_PROCESSING_CALL:
+                    Log.w(LOG_TAG, "Unexpected behavior! New audio processing call appeared while"
+                            + " in audio processing state.");
+                    return HANDLED;
                 case TONE_STARTED_PLAYING:
                     // This shouldn't happen either, but perform the action anyway.
                     Log.w(LOG_TAG, "Tone started playing unexpectedly. Args are: \n"
@@ -249,25 +384,25 @@
                     // Do nothing and keep ringing.
                     return HANDLED;
                 case NO_MORE_RINGING_CALLS:
-                    // If there are active or holding calls, switch to the appropriate focus.
-                    // Otherwise abandon focus.
-                    if (args.hasActiveOrDialingCalls) {
-                        if (args.foregroundCallIsVoip) {
-                            transitionTo(mVoipCallFocusState);
-                        } else {
-                            transitionTo(mSimCallFocusState);
-                        }
-                    } else if (args.hasHoldingCalls || args.isTonePlaying) {
-                        transitionTo(mOtherFocusState);
-                    } else {
-                        transitionTo(mUnfocusedState);
+                    BaseState destState = calculateProperStateFromArgs(args);
+                    if (destState == this) {
+                        Log.w(LOG_TAG, "Got spurious NO_MORE_RINGING_CALLS");
                     }
+                    transitionTo(destState);
                     return HANDLED;
                 case NEW_ACTIVE_OR_DIALING_CALL:
                     // If a call becomes active suddenly, give it priority over ringing.
                     transitionTo(args.foregroundCallIsVoip
                             ? mVoipCallFocusState : mSimCallFocusState);
                     return HANDLED;
+                case NEW_AUDIO_PROCESSING_CALL:
+                    // If we don't have any more ringing calls, transition to audio processing.
+                    if (!args.hasRingingCalls) {
+                        transitionTo(mAudioProcessingFocusState);
+                    } else {
+                        Log.w(LOG_TAG, "Got a audio processing call while there's still a call "
+                                + "ringing");
+                    }
                 case NEW_RINGING_CALL:
                     Log.w(LOG_TAG, "Unexpected behavior! New ringing call appeared while in " +
                             "ringing state.");
@@ -310,7 +445,7 @@
             switch (msg.what) {
                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
                     // Switch to either ringing, holding, or inactive
-                    transitionTo(destinationStateAfterNoMoreActiveCalls(args));
+                    transitionTo(calculateProperStateFromArgs(args));
                     return HANDLED;
                 case NO_MORE_RINGING_CALLS:
                     // Don't transition state, but stop any call-waiting tones that may have been
@@ -323,23 +458,38 @@
                     // indicating that a ringing call has disconnected while this state machine
                     // is in the SimCallFocusState.
                     if (!args.hasActiveOrDialingCalls) {
-                        transitionTo(destinationStateAfterNoMoreActiveCalls(args));
+                        transitionTo(calculateProperStateFromArgs(args));
                     }
                     return HANDLED;
                 case NO_MORE_HOLDING_CALLS:
-                    // Do nothing.
+                    if (args.foregroundCallIsVoip) {
+                        transitionTo(mVoipCallFocusState);
+                    }
                     return HANDLED;
                 case NEW_ACTIVE_OR_DIALING_CALL:
-                    // Do nothing. Already active.
+                    if (args.foregroundCallIsVoip) {
+                        transitionTo(mVoipCallFocusState);
+                    }
                     return HANDLED;
                 case NEW_RINGING_CALL:
                     // Don't make a call ring over an active call, but do play a call waiting tone.
                     mCallAudioManager.startCallWaiting("call already active");
                     return HANDLED;
                 case NEW_HOLDING_CALL:
-                    // Don't do anything now. Putting an active call on hold will be handled when
+                    // Just check the voip mode. Putting an active call on hold will be handled when
                     // NO_MORE_ACTIVE_CALLS is processed.
+                    if (args.foregroundCallIsVoip) {
+                        transitionTo(mVoipCallFocusState);
+                    }
                     return HANDLED;
+                case NEW_AUDIO_PROCESSING_CALL:
+                    // If we don't have any more active calls, transition to audio processing.
+                    if (!args.hasActiveOrDialingCalls) {
+                        transitionTo(mAudioProcessingFocusState);
+                    } else {
+                        Log.w(LOG_TAG, "Got a audio processing call while there's still a call "
+                                + "active");
+                    }
                 case FOREGROUND_VOIP_MODE_CHANGE:
                     if (args.foregroundCallIsVoip) {
                         transitionTo(mVoipCallFocusState);
@@ -372,7 +522,7 @@
             switch (msg.what) {
                 case NO_MORE_ACTIVE_OR_DIALING_CALLS:
                     // Switch to either ringing, holding, or inactive
-                    transitionTo(destinationStateAfterNoMoreActiveCalls(args));
+                    transitionTo(calculateProperStateFromArgs(args));
                     return HANDLED;
                 case NO_MORE_RINGING_CALLS:
                     // Don't transition state, but stop any call-waiting tones that may have been
@@ -382,19 +532,34 @@
                     }
                     return HANDLED;
                 case NO_MORE_HOLDING_CALLS:
-                    // Do nothing.
+                    if (!args.foregroundCallIsVoip) {
+                        transitionTo(mSimCallFocusState);
+                    }
                     return HANDLED;
                 case NEW_ACTIVE_OR_DIALING_CALL:
-                    // Do nothing. Already active.
+                    if (!args.foregroundCallIsVoip) {
+                        transitionTo(mSimCallFocusState);
+                    }
                     return HANDLED;
                 case NEW_RINGING_CALL:
                     // Don't make a call ring over an active call, but do play a call waiting tone.
                     mCallAudioManager.startCallWaiting("call already active");
                     return HANDLED;
                 case NEW_HOLDING_CALL:
-                    // Don't do anything now. Putting an active call on hold will be handled when
+                    // Just check the voip mode. Putting an active call on hold will be handled when
                     // NO_MORE_ACTIVE_CALLS is processed.
+                    if (!args.foregroundCallIsVoip) {
+                        transitionTo(mSimCallFocusState);
+                    }
                     return HANDLED;
+                case NEW_AUDIO_PROCESSING_CALL:
+                    // If we don't have any more active calls, transition to audio processing.
+                    if (!args.hasActiveOrDialingCalls) {
+                        transitionTo(mAudioProcessingFocusState);
+                    } else {
+                        Log.w(LOG_TAG, "Got a audio processing call while there's still a call "
+                                + "active");
+                    }
                 case FOREGROUND_VOIP_MODE_CHANGE:
                     if (!args.foregroundCallIsVoip) {
                         transitionTo(mSimCallFocusState);
@@ -461,7 +626,7 @@
                     mCallAudioManager.stopCallWaiting();
                     return HANDLED;
                 case TONE_STOPPED_PLAYING:
-                    transitionTo(destinationStateAfterNoMoreActiveCalls(args));
+                    transitionTo(calculateProperStateFromArgs(args));
                 default:
                     return NOT_HANDLED;
             }
@@ -474,6 +639,7 @@
     private final BaseState mRingingFocusState = new RingingFocusState();
     private final BaseState mSimCallFocusState = new SimCallFocusState();
     private final BaseState mVoipCallFocusState = new VoipCallFocusState();
+    private final BaseState mAudioProcessingFocusState = new AudioProcessingFocusState();
     private final BaseState mOtherFocusState = new OtherFocusState();
 
     private final AudioManager mAudioManager;
@@ -490,14 +656,39 @@
         mSystemStateHelper = systemStateHelper;
         mMostRecentMode = AudioManager.MODE_NORMAL;
 
+        createStates();
+    }
+
+    /**
+     * Used for testing
+     */
+    public CallAudioModeStateMachine(SystemStateHelper systemStateHelper,
+            AudioManager audioManager, Looper looper) {
+        super(CallAudioModeStateMachine.class.getSimpleName(), looper);
+        mAudioManager = audioManager;
+        mSystemStateHelper = systemStateHelper;
+        mMostRecentMode = AudioManager.MODE_NORMAL;
+
+        createStates();
+    }
+
+    private void createStates() {
         addState(mUnfocusedState);
         addState(mRingingFocusState);
         addState(mSimCallFocusState);
         addState(mVoipCallFocusState);
+        addState(mAudioProcessingFocusState);
         addState(mOtherFocusState);
         setInitialState(mUnfocusedState);
         start();
-        sendMessage(INITIALIZE, new MessageArgs());
+        sendMessage(INITIALIZE, new MessageArgs.Builder()
+                .setHasActiveOrDialingCalls(false)
+                .setHasRingingCalls(false)
+                .setHasHoldingCalls(false)
+                .setIsTonePlaying(false)
+                .setForegroundCallIsVoip(false)
+                .setSession(Log.createSubsession())
+                .build());
     }
 
     public void setCallAudioManager(CallAudioManager callAudioManager) {
@@ -537,15 +728,32 @@
         Log.endSession();
     }
 
-    private BaseState destinationStateAfterNoMoreActiveCalls(MessageArgs args) {
-        if (args.hasHoldingCalls) {
+    private BaseState calculateProperStateFromArgs(MessageArgs args) {
+        // If there are active, audio-processing, holding, or ringing calls,
+        // switch to the appropriate focus.
+        // Otherwise abandon focus.
+
+        // The order matters here. If there are active calls, holding focus for them takes priority.
+        // After that, we want to prioritize holding calls over ringing calls so that when a
+        // call-waiting call gets answered, there's no transition in and out of the ringing focus
+        // state. After that, we want tones since we actually hold focus during them, then the
+        // audio processing state because that will release focus.
+        if (args.hasActiveOrDialingCalls) {
+            if (args.foregroundCallIsVoip) {
+                return mVoipCallFocusState;
+            } else {
+                return mSimCallFocusState;
+            }
+        } else if (args.hasHoldingCalls) {
             return mOtherFocusState;
         } else if (args.hasRingingCalls) {
             return mRingingFocusState;
         } else if (args.isTonePlaying) {
             return mOtherFocusState;
-        } else {
-            return mUnfocusedState;
+        } else if (args.hasAudioProcessingCalls) {
+            return mAudioProcessingFocusState;
         }
+        return mUnfocusedState;
     }
+
 }
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index dcf1b27..2e9917b 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -28,6 +28,7 @@
 import android.media.AudioManager;
 import android.media.IAudioService;
 import android.os.Binder;
+import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -1316,7 +1317,7 @@
     private final WiredHeadsetManager mWiredHeadsetManager;
     private final StatusBarNotifier mStatusBarNotifier;
     private final CallAudioManager.AudioServiceFactory mAudioServiceFactory;
-    private final boolean mDoesDeviceSupportEarpieceRoute;
+    private boolean mDoesDeviceSupportEarpieceRoute;
     private final TelecomSystem.SyncRoot mLock;
     private boolean mHasUserExplicitlyLeftBluetooth = false;
 
@@ -1339,16 +1340,6 @@
             CallAudioManager.AudioServiceFactory audioServiceFactory,
             int earpieceControl) {
         super(NAME);
-        addState(mActiveEarpieceRoute);
-        addState(mActiveHeadsetRoute);
-        addState(mActiveBluetoothRoute);
-        addState(mActiveSpeakerRoute);
-        addState(mRingingBluetoothRoute);
-        addState(mQuiescentEarpieceRoute);
-        addState(mQuiescentHeadsetRoute);
-        addState(mQuiescentBluetoothRoute);
-        addState(mQuiescentSpeakerRoute);
-
         mContext = context;
         mCallsManager = callsManager;
         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
@@ -1356,6 +1347,34 @@
         mWiredHeadsetManager = wiredHeadsetManager;
         mStatusBarNotifier = statusBarNotifier;
         mAudioServiceFactory = audioServiceFactory;
+        mLock = callsManager.getLock();
+
+        createStates(earpieceControl);
+    }
+
+    /** Used for testing only */
+    public CallAudioRouteStateMachine(
+            Context context,
+            CallsManager callsManager,
+            BluetoothRouteManager bluetoothManager,
+            WiredHeadsetManager wiredHeadsetManager,
+            StatusBarNotifier statusBarNotifier,
+            CallAudioManager.AudioServiceFactory audioServiceFactory,
+            int earpieceControl, Looper looper) {
+        super(NAME, looper);
+        mContext = context;
+        mCallsManager = callsManager;
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        mBluetoothRouteManager = bluetoothManager;
+        mWiredHeadsetManager = wiredHeadsetManager;
+        mStatusBarNotifier = statusBarNotifier;
+        mAudioServiceFactory = audioServiceFactory;
+        mLock = callsManager.getLock();
+
+        createStates(earpieceControl);
+    }
+
+    private void createStates(int earpieceControl) {
         switch (earpieceControl) {
             case EARPIECE_FORCE_DISABLED:
                 mDoesDeviceSupportEarpieceRoute = false;
@@ -1366,7 +1385,17 @@
             default:
                 mDoesDeviceSupportEarpieceRoute = checkForEarpieceSupport();
         }
-        mLock = callsManager.getLock();
+
+        addState(mActiveEarpieceRoute);
+        addState(mActiveHeadsetRoute);
+        addState(mActiveBluetoothRoute);
+        addState(mActiveSpeakerRoute);
+        addState(mRingingBluetoothRoute);
+        addState(mQuiescentEarpieceRoute);
+        addState(mQuiescentHeadsetRoute);
+        addState(mQuiescentBluetoothRoute);
+        addState(mQuiescentSpeakerRoute);
+
 
         mStateNameToRouteCode = new HashMap<>(8);
         mStateNameToRouteCode.put(mQuiescentEarpieceRoute.getName(), ROUTE_EARPIECE);
diff --git a/src/com/android/server/telecom/CallIntentProcessor.java b/src/com/android/server/telecom/CallIntentProcessor.java
index 4afa645..aea60df 100644
--- a/src/com/android/server/telecom/CallIntentProcessor.java
+++ b/src/com/android/server/telecom/CallIntentProcessor.java
@@ -38,11 +38,16 @@
     }
 
     public static class AdapterImpl implements Adapter {
+        private final DefaultDialerCache mDefaultDialerCache;
+        public AdapterImpl(DefaultDialerCache cache) {
+            mDefaultDialerCache = cache;
+        }
+
         @Override
         public void processOutgoingCallIntent(Context context, CallsManager callsManager,
                 Intent intent, String callingPackage) {
             CallIntentProcessor.processOutgoingCallIntent(context, callsManager, intent,
-                    callingPackage);
+                    callingPackage, mDefaultDialerCache);
         }
 
         @Override
@@ -58,11 +63,6 @@
 
     public static final String KEY_IS_UNKNOWN_CALL = "is_unknown_call";
     public static final String KEY_IS_INCOMING_CALL = "is_incoming_call";
-    /*
-     *  Whether or not the dialer initiating this outgoing call is the default dialer, or system
-     *  dialer and thus allowed to make emergency calls.
-     */
-    public static final String KEY_IS_PRIVILEGED_DIALER = "is_privileged_dialer";
 
     /**
      * The user initiating the outgoing call.
@@ -72,10 +72,13 @@
 
     private final Context mContext;
     private final CallsManager mCallsManager;
+    private final DefaultDialerCache mDefaultDialerCache;
 
-    public CallIntentProcessor(Context context, CallsManager callsManager) {
+    public CallIntentProcessor(Context context, CallsManager callsManager,
+            DefaultDialerCache defaultDialerCache) {
         this.mContext = context;
         this.mCallsManager = callsManager;
+        this.mDefaultDialerCache = defaultDialerCache;
     }
 
     public void processIntent(Intent intent, String callingPackage) {
@@ -86,7 +89,8 @@
         if (isUnknownCall) {
             processUnknownCallIntent(mCallsManager, intent);
         } else {
-            processOutgoingCallIntent(mContext, mCallsManager, intent, callingPackage);
+            processOutgoingCallIntent(mContext, mCallsManager, intent, callingPackage,
+                    mDefaultDialerCache);
         }
         Trace.endSection();
     }
@@ -102,7 +106,8 @@
             Context context,
             CallsManager callsManager,
             Intent intent,
-            String callingPackage) {
+            String callingPackage,
+            DefaultDialerCache defaultDialerCache) {
 
         Uri handle = intent.getData();
         String scheme = handle.getScheme();
@@ -157,6 +162,9 @@
 
         UserHandle initiatingUser = intent.getParcelableExtra(KEY_INITIATING_USER);
 
+        boolean isPrivilegedDialer = defaultDialerCache.isDefaultOrSystemDialer(callingPackage,
+                initiatingUser.getIdentifier());
+
         // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
         CompletableFuture<Call> callFuture = callsManager
                 .startOutgoingCall(handle, phoneAccountHandle, clientExtras, initiatingUser,
@@ -167,7 +175,8 @@
             if (call != null) {
                 Log.continueSession(logSubsession, "CIP.sNOCI");
                 try {
-                    sendNewOutgoingCallIntent(context, call, callsManager, intent);
+                    sendNewOutgoingCallIntent(context, call, callsManager, intent,
+                            isPrivilegedDialer, defaultDialerCache);
                 } finally {
                     Log.endSession();
                 }
@@ -176,17 +185,15 @@
     }
 
     static void sendNewOutgoingCallIntent(Context context, Call call, CallsManager callsManager,
-            Intent intent) {
+            Intent intent, boolean isPrivilegedDialer, DefaultDialerCache defaultDialerCache) {
         // Asynchronous calls should not usually be made inside a BroadcastReceiver because once
         // onReceive is complete, the BroadcastReceiver's process runs the risk of getting
         // killed if memory is scarce. However, this is OK here because the entire Telecom
         // process will be running throughout the duration of the phone call and should never
         // be killed.
-        final boolean isPrivilegedDialer = intent.getBooleanExtra(KEY_IS_PRIVILEGED_DIALER, false);
-
         NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
                 context, callsManager, call, intent, callsManager.getPhoneNumberUtilsAdapter(),
-                isPrivilegedDialer);
+                isPrivilegedDialer, defaultDialerCache);
 
         // If the broadcaster comes back with an immediate error, disconnect and show a dialog.
         NewOutgoingCallIntentBroadcaster.CallDisposition disposition = broadcaster.evaluateCall();
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index b1b2058..d42c6b5 100755
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -333,7 +333,7 @@
 
         int callFeatures = getCallFeatures(call.getVideoStateHistory(),
                 call.getDisconnectCause().getCode() == DisconnectCause.CALL_PULLED,
-                shouldSaveHdInfo(call, accountHandle),
+                call.wasHighDefAudio(), call.wasWifi(),
                 (call.getConnectionProperties() & Connection.PROPERTY_ASSISTED_DIALING_USED) ==
                         Connection.PROPERTY_ASSISTED_DIALING_USED,
                 call.wasEverRttCall());
@@ -456,11 +456,12 @@
      * @param videoState The video state.
      * @param isPulledCall {@code true} if this call was pulled to another device.
      * @param isStoreHd {@code true} if this call was used HD.
+     * @param isWifi {@code true} if this call was used wifi.
      * @param isUsingAssistedDialing {@code true} if this call used assisted dialing.
      * @return The call features.
      */
     private static int getCallFeatures(int videoState, boolean isPulledCall, boolean isStoreHd,
-            boolean isUsingAssistedDialing, boolean isRtt) {
+            boolean isWifi, boolean isUsingAssistedDialing, boolean isRtt) {
         int features = 0;
         if (VideoProfile.isVideo(videoState)) {
             features |= Calls.FEATURES_VIDEO;
@@ -471,6 +472,9 @@
         if (isStoreHd) {
             features |= Calls.FEATURES_HD_CALL;
         }
+        if (isWifi) {
+            features |= Calls.FEATURES_WIFI;
+        }
         if (isUsingAssistedDialing) {
             features |= Calls.FEATURES_ASSISTED_DIALING_USED;
         }
@@ -480,22 +484,6 @@
         return features;
     }
 
-    private boolean shouldSaveHdInfo(Call call, PhoneAccountHandle accountHandle) {
-        CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(
-                Context.CARRIER_CONFIG_SERVICE);
-        PersistableBundle configBundle = null;
-        if (configManager != null) {
-            configBundle = configManager.getConfigForSubId(
-                    mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(accountHandle));
-        }
-        if (configBundle != null && configBundle.getBoolean(
-                CarrierConfigManager.KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL)
-                && call.wasHighDefAudio()) {
-            return true;
-        }
-        return false;
-    }
-
     /**
      * Retrieve the phone number from the call, and then process it before returning the
      * actual number that is to be logged.
diff --git a/src/com/android/server/telecom/CallScreeningServiceHelper.java b/src/com/android/server/telecom/CallScreeningServiceHelper.java
index a9341ab..79d5286 100644
--- a/src/com/android/server/telecom/CallScreeningServiceHelper.java
+++ b/src/com/android/server/telecom/CallScreeningServiceHelper.java
@@ -68,6 +68,11 @@
         }
 
         @Override
+        public void screenCallFurther(String callId) throws RemoteException {
+            // no-op; we don't allow this on outgoing calls.
+        }
+
+        @Override
         public void disallowCall(String s, boolean b, boolean b1, boolean b2,
                 ComponentName componentName) throws RemoteException {
             // no-op; we don't allow this on outgoing calls.
diff --git a/src/com/android/server/telecom/CallState.java b/src/com/android/server/telecom/CallState.java
index 1e31732..0411ecc 100644
--- a/src/com/android/server/telecom/CallState.java
+++ b/src/com/android/server/telecom/CallState.java
@@ -118,7 +118,19 @@
      * Indicates that an incoming call has been answered by the in-call UI, but Telephony hasn't yet
      * set the call to active.
      */
-    public static final int ANSWERED = 11;
+    public static final int ANSWERED = TelecomProtoEnums.ANSWERED; // = 11
+
+    /**
+     * Indicates that the call is undergoing audio processing by a different app in the background.
+     * @see android.telecom.Call#STATE_AUDIO_PROCESSING
+     */
+    public static final int AUDIO_PROCESSING = TelecomProtoEnums.AUDIO_PROCESSING; // = 12
+
+    /**
+     * Indicates that the call is in a fake ringing state.
+     * @see android.telecom.Call#STATE_SIMULATED_RINGING
+     */
+    public static final int SIMULATED_RINGING = TelecomProtoEnums.SIMULATED_RINGING; // = 13
 
     public static String toString(int callState) {
         switch (callState) {
@@ -146,6 +158,10 @@
                 return "PULLING";
             case ANSWERED:
                 return "ANSWERED";
+            case AUDIO_PROCESSING:
+                return "AUDIO_PROCESSING";
+            case SIMULATED_RINGING:
+                return "SIMULATED_RINGING";
             default:
                 return "UNKNOWN";
         }
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 2bd403c..ca673ad 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -35,6 +35,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.SystemClock;
 import android.os.SystemProperties;
@@ -78,6 +79,7 @@
 import com.android.server.telecom.callfiltering.BlockCheckerAdapter;
 import com.android.server.telecom.callfiltering.CallFilterResultCallback;
 import com.android.server.telecom.callfiltering.CallFilteringResult;
+import com.android.server.telecom.callfiltering.CallFilteringResult.Builder;
 import com.android.server.telecom.callfiltering.CallScreeningServiceController;
 import com.android.server.telecom.callfiltering.DirectToVoicemailCallFilter;
 import com.android.server.telecom.callfiltering.IncomingCallFilter;
@@ -312,6 +314,7 @@
     private final MissedCallNotifier mMissedCallNotifier;
     private IncomingCallNotifier mIncomingCallNotifier;
     private final CallerInfoLookupHelper mCallerInfoLookupHelper;
+    private final IncomingCallFilter.Factory mIncomingCallFilterFactory;
     private final DefaultDialerCache mDefaultDialerCache;
     private final Timeouts.Adapter mTimeoutsAdapter;
     private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
@@ -434,7 +437,8 @@
             CallAudioRouteStateMachine.Factory callAudioRouteStateMachineFactory,
             CallAudioModeStateMachine.Factory callAudioModeStateMachineFactory,
             InCallControllerFactory inCallControllerFactory,
-            RoleManagerAdapter roleManagerAdapter) {
+            RoleManagerAdapter roleManagerAdapter,
+            IncomingCallFilter.Factory incomingCallFilterFactory) {
         mContext = context;
         mLock = lock;
         mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
@@ -450,6 +454,7 @@
         mTimeoutsAdapter = timeoutsAdapter;
         mEmergencyCallHelper = emergencyCallHelper;
         mCallerInfoLookupHelper = callerInfoLookupHelper;
+        mIncomingCallFilterFactory = incomingCallFilterFactory;
 
         mDtmfLocalTonePlayer =
                 new DtmfLocalTonePlayer(new DtmfLocalTonePlayer.ToneGeneratorProxy());
@@ -605,7 +610,12 @@
                     incomingCall.hasProperty(Connection.PROPERTY_EMERGENCY_CALLBACK_MODE),
                     incomingCall.isSelfManaged(),
                     extras.getBoolean(PhoneAccount.EXTRA_SKIP_CALL_FILTERING));
-            onCallFilteringComplete(incomingCall, new CallFilteringResult(true, false, true, true));
+            onCallFilteringComplete(incomingCall, new Builder()
+                    .setShouldAllowCall(true)
+                    .setShouldReject(false)
+                    .setShouldAddToCallLog(true)
+                    .setShouldShowNotification(true)
+                    .build());
             incomingCall.setIsUsingCallFiltering(false);
             return;
         }
@@ -632,7 +642,7 @@
                         return null;
                     }
                 }));
-        new IncomingCallFilter(mContext, this, incomingCall, mLock,
+        mIncomingCallFilterFactory.create(mContext, this, incomingCall, mLock,
                 mTimeoutsAdapter, filters).performFiltering();
     }
 
@@ -1029,7 +1039,8 @@
         mListeners.add(listener);
     }
 
-    void removeListener(CallsManagerListener listener) {
+    @VisibleForTesting
+    public void removeListener(CallsManagerListener listener) {
         mListeners.remove(listener);
     }
 
@@ -1099,9 +1110,11 @@
                 call.setIsVoipAudioMode(true);
             }
         }
-        if (isRttSettingOn() ||
+
+        boolean isRttSettingOn = isRttSettingOn(phoneAccountHandle);
+        if (isRttSettingOn ||
                 extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) {
-            Log.i(this, "Incoming call requesting RTT, rtt setting is %b", isRttSettingOn());
+            Log.i(this, "Incoming call requesting RTT, rtt setting is %b", isRttSettingOn);
             call.createRttStreams();
             // Even if the phone account doesn't support RTT yet, the connection manager might
             // change that. Set this to check it later.
@@ -1532,11 +1545,12 @@
 
                     boolean isVoicemail = isVoicemail(callToUse.getHandle(), accountToUse);
 
-                    if (!isVoicemail && (isRttSettingOn() || (extras != null
+                    boolean isRttSettingOn = isRttSettingOn(phoneAccountHandle);
+                    if (!isVoicemail && (isRttSettingOn || (extras != null
                             && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT,
                             false)))) {
                         Log.d(this, "Outgoing call requesting RTT, rtt setting is %b",
-                                isRttSettingOn());
+                                isRttSettingOn);
                         if (callToUse.isEmergencyCall() || (accountToUse != null
                                 && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT))) {
                             // If the call requested RTT and it's an emergency call, ignore the
@@ -1619,6 +1633,7 @@
      * @param handle The handle of the outgoing call; used to determine the SIP scheme when matching
      *               phone accounts.
      * @param isVideo {@code true} if the call is a video call, {@code false} otherwise.
+     * @param isEmergency {@code true} if the call is an emergency call.
      * @param initiatingUser The {@link UserHandle} the call is placed on.
      * @return
      */
@@ -1749,7 +1764,7 @@
             Log.w(this, "onCallRedirectionComplete: phoneAccountHandle is null");
             endEarly = true;
             disconnectReason = "Null phoneAccountHandle from Call Redirection Service";
-        } else if (mPhoneNumberUtilsAdapter.isPotentialLocalEmergencyNumber(mContext,
+        } else if (getTelephonyManager().isPotentialEmergencyNumber(
                 handle.getSchemeSpecificPart())) {
             Log.w(this, "onCallRedirectionComplete: emergency number %s is redirected from Call"
                     + " Redirection Service", handle.getSchemeSpecificPart());
@@ -2221,10 +2236,8 @@
                         isEmergency ? 0 : PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY);
         // First check the Radio SIM Technology
         if(mRadioSimVariants == null) {
-            TelephonyManager tm = (TelephonyManager) mContext.getSystemService(
-                    Context.TELEPHONY_SERVICE);
             // Cache Sim Variants
-            mRadioSimVariants = tm.getMultiSimConfiguration();
+            mRadioSimVariants = getTelephonyManager().getMultiSimConfiguration();
         }
         // Only one SIM PhoneAccount can be active at one time for DSDS. Only that SIM PhoneAccount
         // Should be available if a call is already active on the SIM account.
@@ -2248,6 +2261,10 @@
         return allAccounts;
     }
 
+    private TelephonyManager getTelephonyManager() {
+        return mContext.getSystemService(TelephonyManager.class);
+    }
+
     /**
      * Informs listeners (notably {@link CallAudioManager} of a change to the call's external
      * property.
@@ -2299,11 +2316,6 @@
       * speaker phone.
       */
     void setAudioRoute(int route, String bluetoothAddress) {
-        if (hasEmergencyRttCall() && route != CallAudioState.ROUTE_SPEAKER) {
-            Log.i(this, "In an emergency RTT call. Forcing route to speaker.");
-            route = CallAudioState.ROUTE_SPEAKER;
-            bluetoothAddress = null;
-        }
         mCallAudioManager.setAudioRoute(route, bluetoothAddress);
     }
 
@@ -2321,9 +2333,22 @@
         mProximitySensorManager.turnOff(screenOnImmediately);
     }
 
-    private boolean isRttSettingOn() {
-        return Settings.Secure.getInt(mContext.getContentResolver(),
+    private boolean isRttSettingOn(PhoneAccountHandle handle) {
+        boolean isRttModeSettingOn = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.RTT_CALLING_MODE, 0) != 0;
+        // If the carrier config says that we should ignore the RTT mode setting from the user,
+        // assume that it's off (i.e. only make an RTT call if it's requested through the extra).
+        boolean shouldIgnoreRttModeSetting = getCarrierConfigForPhoneAccount(handle)
+                .getBoolean(CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL, false);
+        return isRttModeSettingOn && !shouldIgnoreRttModeSetting;
+    }
+
+    private PersistableBundle getCarrierConfigForPhoneAccount(PhoneAccountHandle handle) {
+        int subscriptionId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(handle);
+        CarrierConfigManager carrierConfigManager =
+                mContext.getSystemService(CarrierConfigManager.class);
+        PersistableBundle result = carrierConfigManager.getConfigForSubId(subscriptionId);
+        return result == null ? new PersistableBundle() : result;
     }
 
     void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) {
diff --git a/src/com/android/server/telecom/ConnectionServiceWrapper.java b/src/com/android/server/telecom/ConnectionServiceWrapper.java
index 4621558..911cf0f 100644
--- a/src/com/android/server/telecom/ConnectionServiceWrapper.java
+++ b/src/com/android/server/telecom/ConnectionServiceWrapper.java
@@ -1743,8 +1743,12 @@
             }
             ConnectionServiceWrapper service = mConnectionServiceRepository.getService(
                     handle.getComponentName(), handle.getUserHandle());
-            if (service != null) {
+            if (service != null && service != this) {
                 simServices.add(service);
+            } else {
+                // This is unexpected, normally PhoneAccounts with CAPABILITY_CALL_PROVIDER are not
+                // also CAPABILITY_CONNECTION_MANAGER
+                Log.w(this, "call provider also detected as SIM call manager: " + service);
             }
         }
 
@@ -1761,11 +1765,6 @@
         Log.i(this, "queryRemoteConnectionServices, simServices = %s", simServices);
 
         for (ConnectionServiceWrapper simService : simServices) {
-            if (simService == this) {
-                // Only happens in the unlikely case that a SIM service is also a SIM call manager
-                continue;
-            }
-
             final ConnectionServiceWrapper currentSimService = simService;
 
             currentSimService.mBinder.bind(new BindCallback() {
diff --git a/src/com/android/server/telecom/ContactsAsyncHelper.java b/src/com/android/server/telecom/ContactsAsyncHelper.java
index 7fb6419..37ee941 100644
--- a/src/com/android/server/telecom/ContactsAsyncHelper.java
+++ b/src/com/android/server/telecom/ContactsAsyncHelper.java
@@ -40,6 +40,12 @@
 public class ContactsAsyncHelper {
     private static final String LOG_TAG = ContactsAsyncHelper.class.getSimpleName();
 
+    public static class Factory {
+        public ContactsAsyncHelper create(ContentResolverAdapter adapter) {
+            return new ContactsAsyncHelper(adapter);
+        }
+    }
+
     /**
      * Interface for a WorkerHandler result return.
      */
@@ -77,6 +83,11 @@
         mContentResolverAdapter = contentResolverAdapter;
     }
 
+    public ContactsAsyncHelper(ContentResolverAdapter contentResolverAdapter, Looper looper) {
+        mContentResolverAdapter = contentResolverAdapter;
+        mThreadHandler = new WorkerHandler(looper);
+    }
+
     private static final class WorkerArgs {
         public Context context;
         public Uri displayPhotoUri;
diff --git a/src/com/android/server/telecom/CreateConnectionProcessor.java b/src/com/android/server/telecom/CreateConnectionProcessor.java
index 6c92724..84e97d9 100644
--- a/src/com/android/server/telecom/CreateConnectionProcessor.java
+++ b/src/com/android/server/telecom/CreateConnectionProcessor.java
@@ -383,6 +383,12 @@
                 allAccounts.add(TelephonyUtil.getDefaultEmergencyPhoneAccount());
             }
 
+            // When testing emergency calls, we want the calls to go through to the test connection
+            // service, not the telephony ConnectionService.
+            if (mCall.isTestEmergencyCall()) {
+                allAccounts = mPhoneAccountRegistrar.filterRestrictedPhoneAccounts(allAccounts);
+            }
+
             // Get user preferred PA if it exists.
             PhoneAccount preferredPA = mPhoneAccountRegistrar.getPhoneAccountUnchecked(
                     preferredPAH);
diff --git a/src/com/android/server/telecom/DefaultDialerCache.java b/src/com/android/server/telecom/DefaultDialerCache.java
index b72d860..c2b78ee 100644
--- a/src/com/android/server/telecom/DefaultDialerCache.java
+++ b/src/com/android/server/telecom/DefaultDialerCache.java
@@ -18,9 +18,11 @@
 
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Handler;
@@ -134,9 +136,10 @@
     private final Context mContext;
     private final DefaultDialerManagerAdapter mDefaultDialerManagerAdapter;
     private final TelecomSystem.SyncRoot mLock;
-    private final String mSystemDialerName;
+    private final ComponentName mSystemDialerComponentName;
     private final RoleManagerAdapter mRoleManagerAdapter;
     private SparseArray<String> mCurrentDefaultDialerPerUser = new SparseArray<>();
+    private ComponentName mOverrideSystemDialerComponentName;
 
     public DefaultDialerCache(Context context,
             DefaultDialerManagerAdapter defaultDialerManagerAdapter,
@@ -146,7 +149,11 @@
         mDefaultDialerManagerAdapter = defaultDialerManagerAdapter;
         mRoleManagerAdapter = roleManagerAdapter;
         mLock = lock;
-        mSystemDialerName = TelecomServiceImpl.getSystemDialerPackage(mContext);
+        Resources resources = mContext.getResources();
+        mSystemDialerComponentName = new ComponentName(resources.getString(
+                com.android.internal.R.string.config_defaultDialer),
+                resources.getString(R.string.incall_default_class));
+
 
         IntentFilter packageIntentFilter = new IntentFilter();
         packageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
@@ -194,6 +201,22 @@
         return getDefaultDialerApplication(mContext.getUserId());
     }
 
+    public void setSystemDialerComponentName(ComponentName testComponentName) {
+        mOverrideSystemDialerComponentName = testComponentName;
+    }
+
+    public String getSystemDialerApplication() {
+        if (mOverrideSystemDialerComponentName != null) {
+            return mOverrideSystemDialerComponentName.getPackageName();
+        }
+        return mSystemDialerComponentName.getPackageName();
+    }
+
+    public ComponentName getSystemDialerComponent() {
+        if (mOverrideSystemDialerComponentName != null) return mOverrideSystemDialerComponentName;
+        return mSystemDialerComponentName;
+    }
+
     public void observeDefaultDialerApplication(Executor executor, IntConsumer observer) {
         mRoleManagerAdapter.observeDefaultDialerApp(executor, observer);
     }
@@ -201,7 +224,7 @@
     public boolean isDefaultOrSystemDialer(String packageName, int userId) {
         String defaultDialer = getDefaultDialerApplication(userId);
         return Objects.equals(packageName, defaultDialer)
-                || Objects.equals(packageName, mSystemDialerName);
+                || Objects.equals(packageName, getSystemDialerApplication());
     }
 
     public boolean setDefaultDialer(String packageName, int userId) {
diff --git a/src/com/android/server/telecom/EmergencyCallHelper.java b/src/com/android/server/telecom/EmergencyCallHelper.java
index 7052597..5de4e5a 100644
--- a/src/com/android/server/telecom/EmergencyCallHelper.java
+++ b/src/com/android/server/telecom/EmergencyCallHelper.java
@@ -25,13 +25,13 @@
 
 /**
  * Helps with emergency calls by:
- * 1. granting temporary location permission to the default dialer service during emergency calls
+ * 1. granting temporary location permission to the system dialer service during emergency calls
  * 2. keeping track of the time of the last emergency call
  */
 @VisibleForTesting
 public class EmergencyCallHelper {
     private final Context mContext;
-    private final String mDefaultDialerPackage;
+    private final DefaultDialerCache mDefaultDialerCache;
     private final Timeouts.Adapter mTimeoutsAdapter;
     private UserHandle mLocationPermissionGrantedToUser;
     private boolean mHadFineLocation = false;
@@ -41,10 +41,10 @@
     @VisibleForTesting
     public EmergencyCallHelper(
             Context context,
-            String defaultDialerPackage,
+            DefaultDialerCache defaultDialerCache,
             Timeouts.Adapter timeoutsAdapter) {
         mContext = context;
-        mDefaultDialerPackage = defaultDialerPackage;
+        mDefaultDialerCache = defaultDialerCache;
         mTimeoutsAdapter = timeoutsAdapter;
     }
 
@@ -94,48 +94,50 @@
     }
 
     private void grantLocationPermission(UserHandle userHandle) {
-        Log.i(this, "Granting temporary location permission to " + mDefaultDialerPackage
+        String systemDialerPackage = mDefaultDialerCache.getSystemDialerApplication();
+        Log.i(this, "Granting temporary location permission to " + systemDialerPackage
               + ", user: " + userHandle);
         try {
             boolean hadBackgroundLocation = hasBackgroundLocationPermission();
             boolean hadFineLocation = hasFineLocationPermission();
             if (hadBackgroundLocation && hadFineLocation) {
-                Log.i(this, "Skipping location grant because the default dialer already"
+                Log.i(this, "Skipping location grant because the system dialer already"
                         + " holds sufficient permissions");
                 return;
             }
             if (!hadFineLocation) {
-                mContext.getPackageManager().grantRuntimePermission(mDefaultDialerPackage,
+                mContext.getPackageManager().grantRuntimePermission(systemDialerPackage,
                         Manifest.permission.ACCESS_FINE_LOCATION, userHandle);
             }
             if (!hadBackgroundLocation) {
-                mContext.getPackageManager().grantRuntimePermission(mDefaultDialerPackage,
+                mContext.getPackageManager().grantRuntimePermission(systemDialerPackage,
                         Manifest.permission.ACCESS_BACKGROUND_LOCATION, userHandle);
             }
             mHadFineLocation = hadFineLocation;
             mHadBackgroundLocation = hadBackgroundLocation;
             recordPermissionGrant(userHandle);
         } catch (Exception e) {
-            Log.e(this, e, "Failed to grant location permissions to " + mDefaultDialerPackage
+            Log.e(this, e, "Failed to grant location permissions to " + systemDialerPackage
                   + ", user: " + userHandle);
         }
     }
 
     private void revokeLocationPermission() {
-        Log.i(this, "Revoking temporary location permission from " + mDefaultDialerPackage
+        String systemDialerPackage = mDefaultDialerCache.getSystemDialerApplication();
+        Log.i(this, "Revoking temporary location permission from " + systemDialerPackage
               + ", user: " + mLocationPermissionGrantedToUser);
         UserHandle userHandle = mLocationPermissionGrantedToUser;
         try {
             if (!mHadFineLocation) {
-                mContext.getPackageManager().revokeRuntimePermission(mDefaultDialerPackage,
+                mContext.getPackageManager().revokeRuntimePermission(systemDialerPackage,
                         Manifest.permission.ACCESS_FINE_LOCATION, userHandle);
             }
             if (!mHadBackgroundLocation) {
-                mContext.getPackageManager().revokeRuntimePermission(mDefaultDialerPackage,
+                mContext.getPackageManager().revokeRuntimePermission(systemDialerPackage,
                         Manifest.permission.ACCESS_BACKGROUND_LOCATION, userHandle);
             }
         } catch (Exception e) {
-            Log.e(this, e, "Failed to revoke location permission from " + mDefaultDialerPackage
+            Log.e(this, e, "Failed to revoke location permission from " + systemDialerPackage
                   + ", user: " + userHandle);
         }
         clearPermissionGrant();
@@ -143,13 +145,15 @@
 
     private boolean hasBackgroundLocationPermission() {
         return mContext.getPackageManager().checkPermission(
-                Manifest.permission.ACCESS_BACKGROUND_LOCATION, mDefaultDialerPackage)
+                Manifest.permission.ACCESS_BACKGROUND_LOCATION,
+                mDefaultDialerCache.getSystemDialerApplication())
                 == PackageManager.PERMISSION_GRANTED;
     }
 
     private boolean hasFineLocationPermission() {
         return mContext.getPackageManager().checkPermission(
-                Manifest.permission.ACCESS_FINE_LOCATION, mDefaultDialerPackage)
+                Manifest.permission.ACCESS_FINE_LOCATION,
+                mDefaultDialerCache.getSystemDialerApplication())
                 == PackageManager.PERMISSION_GRANTED;
     }
 
diff --git a/src/com/android/server/telecom/InCallAdapter.java b/src/com/android/server/telecom/InCallAdapter.java
index 8de27be..888010d 100644
--- a/src/com/android/server/telecom/InCallAdapter.java
+++ b/src/com/android/server/telecom/InCallAdapter.java
@@ -19,6 +19,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.telecom.Log;
 import android.telecom.PhoneAccountHandle;
 
@@ -319,6 +320,16 @@
     }
 
     @Override
+    public void enterBackgroundAudioProcessing(String callId) {
+        // TODO: implement this
+    }
+
+    @Override
+    public void exitBackgroundAudioProcessing(String callId, boolean shouldRing) {
+        // TODO: implement this
+    }
+
+    @Override
     public void conference(String callId, String otherCallId) {
         try {
             Log.startSession(LogUtils.Sessions.ICA_CONFERENCE, mOwnerComponentName);
diff --git a/src/com/android/server/telecom/InCallController.java b/src/com/android/server/telecom/InCallController.java
index 2e3ee8d..c4df1d3 100644
--- a/src/com/android/server/telecom/InCallController.java
+++ b/src/com/android/server/telecom/InCallController.java
@@ -721,9 +721,6 @@
 
     private final CallIdMapper mCallIdMapper = new CallIdMapper(Call::getId);
 
-    /** The {@link ComponentName} of the default InCall UI. */
-    private final ComponentName mSystemInCallComponentName;
-
     private final Context mContext;
     private final TelecomSystem.SyncRoot mLock;
     private final CallsManager mCallsManager;
@@ -731,6 +728,7 @@
     private final Timeouts.Adapter mTimeoutsAdapter;
     private final DefaultDialerCache mDefaultDialerCache;
     private final EmergencyCallHelper mEmergencyCallHelper;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
     private CarSwappingInCallServiceConnection mInCallServiceConnection;
     private NonUIInCallServiceConnectionCollection mNonUIInCallServiceConnections;
 
@@ -750,11 +748,6 @@
         mDefaultDialerCache = defaultDialerCache;
         mEmergencyCallHelper = emergencyCallHelper;
 
-        Resources resources = mContext.getResources();
-        mSystemInCallComponentName = new ComponentName(
-                TelecomServiceImpl.getSystemDialerPackage(mContext),
-                resources.getString(R.string.incall_default_class));
-
         mSystemStateHelper.addListener(mSystemStateListener);
     }
 
@@ -817,8 +810,7 @@
             /** Let's add a 2 second delay before we send unbind to the services to hopefully
              *  give them enough time to process all the pending messages.
              */
-            Handler handler = new Handler(Looper.getMainLooper());
-            handler.postDelayed(new Runnable("ICC.oCR", mLock) {
+            mHandler.postDelayed(new Runnable("ICC.oCR", mLock) {
                 @Override
                 public void loggedRun() {
                     // Check again to make sure there are no active calls.
@@ -1106,13 +1098,13 @@
             Log.i(this, "defaultDialer: " + defaultDialerComponentInfo);
             if (defaultDialerComponentInfo != null &&
                     !defaultDialerComponentInfo.getComponentName().equals(
-                            mSystemInCallComponentName)) {
+                            mDefaultDialerCache.getSystemDialerComponent())) {
                 dialerInCall = new InCallServiceBindingConnection(defaultDialerComponentInfo);
             }
             Log.i(this, "defaultDialer: " + dialerInCall);
 
             InCallServiceInfo systemInCallInfo = getInCallServiceComponent(
-                    mSystemInCallComponentName, IN_CALL_SERVICE_TYPE_SYSTEM_UI);
+                    mDefaultDialerCache.getSystemDialerComponent(), IN_CALL_SERVICE_TYPE_SYSTEM_UI);
             EmergencyInCallServiceConnection systemInCall =
                     new EmergencyInCallServiceConnection(systemInCallInfo, dialerInCall);
             systemInCall.setHasEmergency(mCallsManager.hasEmergencyCall());
@@ -1120,7 +1112,8 @@
             InCallServiceConnection carModeInCall = null;
             InCallServiceInfo carModeComponentInfo = getCarModeComponent();
             if (carModeComponentInfo != null &&
-                    !carModeComponentInfo.getComponentName().equals(mSystemInCallComponentName)) {
+                    !carModeComponentInfo.getComponentName().equals(
+                            mDefaultDialerCache.getSystemDialerComponent())) {
                 carModeInCall = new InCallServiceBindingConnection(carModeComponentInfo);
             }
 
@@ -1280,8 +1273,9 @@
             return IN_CALL_SERVICE_TYPE_INVALID;
         }
 
-        if (mSystemInCallComponentName.getPackageName().equals(serviceInfo.packageName) &&
-                mSystemInCallComponentName.getClassName().equals(serviceInfo.name)) {
+        if (mDefaultDialerCache.getSystemDialerApplication().equals(serviceInfo.packageName) &&
+                mDefaultDialerCache.getSystemDialerComponent().getClassName()
+                        .equals(serviceInfo.name)) {
             return IN_CALL_SERVICE_TYPE_SYSTEM_UI;
         }
 
@@ -1597,4 +1591,9 @@
         childCalls.addAll(parentCalls);
         return childCalls;
     }
+
+    @VisibleForTesting
+    public Handler getHandler() {
+        return mHandler;
+    }
 }
diff --git a/src/com/android/server/telecom/InCallTonePlayer.java b/src/com/android/server/telecom/InCallTonePlayer.java
index 5864ce0..4f0cf8d 100644
--- a/src/com/android/server/telecom/InCallTonePlayer.java
+++ b/src/com/android/server/telecom/InCallTonePlayer.java
@@ -502,7 +502,7 @@
             public void loggedRun() {
                 if (sTonesPlaying == 0) {
                     Log.wtf(this, "Over-releasing focus for tone player.");
-                } else if (--sTonesPlaying == 0) {
+                } else if (--sTonesPlaying == 0 && mCallAudioManager != null) {
                     mCallAudioManager.setIsTonePlaying(false);
                 }
             }
diff --git a/src/com/android/server/telecom/LogUtils.java b/src/com/android/server/telecom/LogUtils.java
index 1e82fc0..f55fa2c 100644
--- a/src/com/android/server/telecom/LogUtils.java
+++ b/src/com/android/server/telecom/LogUtils.java
@@ -137,6 +137,7 @@
         public static final String BIND_SCREENING = "BIND_SCREENING";
         public static final String SCREENING_BOUND = "SCREENING_BOUND";
         public static final String SCREENING_SENT = "SCREENING_SENT";
+        public static final String SCREENING_SKIPPED = "SCREENING_SKIPPED";
         public static final String CONTROLLER_SCREENING_COMPLETED =
                 "CONTROLLER_SCREENING_COMPLETED";
         public static final String SCREENING_COMPLETED = "SCREENING_COMPLETED";
diff --git a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
index 7a641af..1abd6fb 100644
--- a/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
+++ b/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java
@@ -35,6 +35,7 @@
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.DisconnectCause;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -76,6 +77,7 @@
     private final Context mContext;
     private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
     private final TelecomSystem.SyncRoot mLock;
+    private final DefaultDialerCache mDefaultDialerCache;
 
     /*
      * Whether or not the outgoing call intent originated from the default phone application. If
@@ -99,7 +101,7 @@
     @VisibleForTesting
     public NewOutgoingCallIntentBroadcaster(Context context, CallsManager callsManager, Call call,
             Intent intent, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
-            boolean isDefaultPhoneApp) {
+            boolean isDefaultPhoneApp, DefaultDialerCache defaultDialerCache) {
         mContext = context;
         mCallsManager = callsManager;
         mCall = call;
@@ -107,6 +109,7 @@
         mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
         mIsDefaultOrSystemPhoneApp = isDefaultPhoneApp;
         mLock = mCallsManager.getLock();
+        mDefaultDialerCache = defaultDialerCache;
     }
 
     /**
@@ -137,8 +140,7 @@
                         disconnectTimeout = getDisconnectTimeoutFromApp(
                                 getResultExtras(false), disconnectTimeout);
                         endEarly = true;
-                    } else if (mPhoneNumberUtilsAdapter.isPotentialLocalEmergencyNumber(
-                            mContext, resultNumber)) {
+                    } else if (isPotentialEmergencyNumber(resultNumber)) {
                         Log.w(this, "Cannot modify outgoing call to emergency number %s.",
                                 resultNumber);
                         disconnectTimeout = 0;
@@ -492,10 +494,7 @@
 
     private void launchSystemDialer(Uri handle) {
         Intent systemDialerIntent = new Intent();
-        final Resources resources = mContext.getResources();
-        systemDialerIntent.setClassName(
-                TelecomServiceImpl.getSystemDialerPackage(mContext),
-                resources.getString(R.string.dialer_default_class));
+        systemDialerIntent.setComponent(mDefaultDialerCache.getSystemDialerComponent());
         systemDialerIntent.setAction(Intent.ACTION_DIAL);
         systemDialerIntent.setData(handle);
         systemDialerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -519,8 +518,8 @@
      */
     private boolean isPotentialEmergencyNumber(String number) {
         Log.v(this, "Checking restrictions for number : %s", Log.pii(number));
-        return (number != null)
-                && mPhoneNumberUtilsAdapter.isPotentialLocalEmergencyNumber(mContext, number);
+        if (number == null) return false;
+        return mContext.getSystemService(TelephonyManager.class).isPotentialEmergencyNumber(number);
     }
 
     /**
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index 8746de1..8776ffd 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -154,6 +154,7 @@
     private final AppLabelProxy mAppLabelProxy;
     private State mState;
     private UserHandle mCurrentUserHandle;
+    private String mTestPhoneAccountPackageNameFilter;
     private interface PhoneAccountRegistrarWriteLock {}
     private final PhoneAccountRegistrarWriteLock mWriteLock =
             new PhoneAccountRegistrarWriteLock() {};
@@ -458,6 +459,34 @@
     }
 
     /**
+     * Sets a filter for which {@link PhoneAccount}s will be returned from
+     * {@link #filterRestrictedPhoneAccounts(List)}. If non-null, only {@link PhoneAccount}s
+     * with the package name packageNameFilter will be returned. If null, no filter is set.
+     * @param packageNameFilter The package name that will be used to filter only
+     * {@link PhoneAccount}s with the same package name.
+     */
+    public void setTestPhoneAccountPackageNameFilter(String packageNameFilter) {
+        mTestPhoneAccountPackageNameFilter = packageNameFilter;
+        Log.i(this, "filter set for PhoneAccounts, packageName=" + packageNameFilter);
+    }
+
+    /**
+     * Filter the given {@link List<PhoneAccount>} and keep only {@link PhoneAccount}s that have the
+     * #mTestPhoneAccountPackageNameFilter.
+     * @param accounts List of {@link PhoneAccount}s to filter.
+     * @return new list of filtered {@link PhoneAccount}s.
+     */
+    public List<PhoneAccount> filterRestrictedPhoneAccounts(List<PhoneAccount> accounts) {
+        if (TextUtils.isEmpty(mTestPhoneAccountPackageNameFilter)) {
+            return new ArrayList<>(accounts);
+        }
+        // Remove all PhoneAccounts that do not have the same package name as the filter.
+        return accounts.stream().filter(account -> mTestPhoneAccountPackageNameFilter.equals(
+                account.getAccountHandle().getComponentName().getPackageName()))
+                .collect(Collectors.toList());
+    }
+
+    /**
      * If it is a outgoing call, sim call manager associated with the target phone account of the
      * call is returned (if one exists).
      * Otherwise, we return the sim call manager of the user associated with the
@@ -1202,6 +1231,9 @@
                 pw.println(phoneAccount);
             }
             pw.decreaseIndent();
+            pw.increaseIndent();
+            pw.println("test emergency PhoneAccount filter: " + mTestPhoneAccountPackageNameFilter);
+            pw.decreaseIndent();
         }
     }
 
diff --git a/src/com/android/server/telecom/PhoneNumberUtilsAdapter.java b/src/com/android/server/telecom/PhoneNumberUtilsAdapter.java
index aa568a9..28d832d 100644
--- a/src/com/android/server/telecom/PhoneNumberUtilsAdapter.java
+++ b/src/com/android/server/telecom/PhoneNumberUtilsAdapter.java
@@ -24,8 +24,6 @@
  * refactoring.
  */
 public interface PhoneNumberUtilsAdapter {
-    boolean isLocalEmergencyNumber(Context context, String number);
-    boolean isPotentialLocalEmergencyNumber(Context context, String number);
     boolean isUriNumber(String number);
     boolean isSamePhoneNumber(String number1, String number2);
     String getNumberFromIntent(Intent intent, Context context);
diff --git a/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java b/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java
index 8b3c856..517857b 100644
--- a/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java
+++ b/src/com/android/server/telecom/PhoneNumberUtilsAdapterImpl.java
@@ -23,20 +23,6 @@
 
 public class PhoneNumberUtilsAdapterImpl implements PhoneNumberUtilsAdapter {
     @Override
-    public boolean isLocalEmergencyNumber(Context context, String number) {
-        TelephonyManager tm = (TelephonyManager) context.getSystemService(
-                Context.TELEPHONY_SERVICE);
-        return tm.isEmergencyNumber(number);
-    }
-
-    @Override
-    public boolean isPotentialLocalEmergencyNumber(Context context, String number) {
-        TelephonyManager tm = (TelephonyManager) context.getSystemService(
-                Context.TELEPHONY_SERVICE);
-        return tm.isPotentialEmergencyNumber(number);
-    }
-
-    @Override
     public boolean isUriNumber(String number) {
         return PhoneNumberUtils.isUriNumber(number);
     }
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index 93f6908..2469c1e 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -374,12 +374,12 @@
                     && mSystemSettingsUtil.enableRampingRingerFromDeviceConfig()
                     && isRingerAudible) {
                 Log.i(this, "start vibration for ramping ringer.");
-                mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
                 mIsVibrating = true;
+                mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
             } else {
                 Log.i(this, "start normal vibration.");
-                mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
                 mIsVibrating = true;
+                mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES);
             }
         } else if (mIsVibrating) {
             Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION, "already vibrating");
diff --git a/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java b/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
index ca44cd4..8d2a2c5 100644
--- a/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
+++ b/src/com/android/server/telecom/TelecomBroadcastIntentProcessor.java
@@ -18,12 +18,17 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.os.UserHandle;
 import android.telecom.Log;
+import android.widget.Toast;
 
 import com.android.server.telecom.ui.CallRedirectionConfirmDialogActivity;
 import com.android.server.telecom.ui.ConfirmCallDialogActivity;
 
+import java.util.List;
+
 public final class TelecomBroadcastIntentProcessor {
     /** The action used to send SMS response for the missed call notification. */
     public static final String ACTION_SEND_SMS_FROM_NOTIFICATION =
@@ -119,7 +124,15 @@
 
                 Intent callIntent = new Intent(Intent.ACTION_SENDTO, intent.getData());
                 callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                mContext.startActivityAsUser(callIntent, userHandle);
+                PackageManager packageManager = mContext.getPackageManager();
+                List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser(
+                        callIntent, PackageManager.MATCH_DEFAULT_ONLY, userHandle.getIdentifier());
+                if (activities.size() > 0) {
+                    mContext.startActivityAsUser(callIntent, userHandle);
+                } else {
+                    Toast.makeText(mContext, com.android.internal.R.string.noApplications,
+                            Toast.LENGTH_SHORT).show();
+                }
 
                 // Call back recent caller from the missed call notification.
             } else if (ACTION_CALL_BACK_FROM_NOTIFICATION.equals(action)) {
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 8bf42a8..f82215f 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -34,7 +34,6 @@
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -581,28 +580,28 @@
         public String getVoiceMailNumber(PhoneAccountHandle accountHandle, String callingPackage) {
             try {
                 Log.startSession("TSI.gVMN");
-                synchronized (mLock) {
-                    if (!canReadPhoneState(callingPackage, "getVoiceMailNumber")) {
+                if (!canReadPhoneState(callingPackage, "getVoiceMailNumber")) {
+                    return null;
+                }
+                try {
+                    final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                    if (!isPhoneAccountHandleVisibleToCallingUser(accountHandle,
+                            callingUserHandle)) {
+                        Log.d(this, "%s is not visible for the calling user [gVMN]",
+                                accountHandle);
                         return null;
                     }
-                    try {
-                        final UserHandle callingUserHandle = Binder.getCallingUserHandle();
-                        if (!isPhoneAccountHandleVisibleToCallingUser(accountHandle,
-                                callingUserHandle)) {
-                            Log.d(this, "%s is not visible for the calling user [gVMN]",
-                                    accountHandle);
-                            return null;
-                        }
-                        int subId = mSubscriptionManagerAdapter.getDefaultVoiceSubId();
+                    int subId = mSubscriptionManagerAdapter.getDefaultVoiceSubId();
+                    synchronized (mLock) {
                         if (accountHandle != null) {
                             subId = mPhoneAccountRegistrar
                                     .getSubscriptionIdForPhoneAccount(accountHandle);
                         }
-                        return getTelephonyManager().getVoiceMailNumber(subId);
-                    } catch (Exception e) {
-                        Log.e(this, e, "getSubscriptionIdForPhoneAccount");
-                        throw e;
                     }
+                    return getTelephonyManager().getVoiceMailNumber(subId);
+                } catch (Exception e) {
+                    Log.e(this, e, "getSubscriptionIdForPhoneAccount");
+                    throw e;
                 }
             } finally {
                 Log.endSession();
@@ -620,25 +619,26 @@
                     return null;
                 }
 
-                synchronized (mLock) {
-                    final UserHandle callingUserHandle = Binder.getCallingUserHandle();
-                    if (!isPhoneAccountHandleVisibleToCallingUser(accountHandle,
-                            callingUserHandle)) {
-                        Log.d(this, "%s is not visible for the calling user [gL1N]", accountHandle);
-                        return null;
-                    }
+                final UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                if (!isPhoneAccountHandleVisibleToCallingUser(accountHandle,
+                        callingUserHandle)) {
+                    Log.d(this, "%s is not visible for the calling user [gL1N]", accountHandle);
+                    return null;
+                }
 
-                    long token = Binder.clearCallingIdentity();
-                    try {
-                        int subId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(
+                long token = Binder.clearCallingIdentity();
+                try {
+                    int subId;
+                    synchronized (mLock) {
+                        subId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(
                                 accountHandle);
-                        return getTelephonyManager().getLine1Number(subId);
-                    } catch (Exception e) {
-                        Log.e(this, e, "getSubscriptionIdForPhoneAccount");
-                        throw e;
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
                     }
+                    return getTelephonyManager().getLine1Number(subId);
+                } catch (Exception e) {
+                    Log.e(this, e, "getSubscriptionIdForPhoneAccount");
+                    throw e;
+                } finally {
+                    Binder.restoreCallingIdentity(token);
                 }
             } finally {
                 Log.endSession();
@@ -678,11 +678,7 @@
         public ComponentName getDefaultPhoneApp() {
             try {
                 Log.startSession("TSI.gDPA");
-                // No need to synchronize
-                Resources resources = mContext.getResources();
-                return new ComponentName(
-                        TelecomServiceImpl.getSystemDialerPackage(mContext),
-                        resources.getString(R.string.dialer_default_class));
+                return mDefaultDialerCache.getSystemDialerComponent();
             } finally {
                 Log.endSession();
             }
@@ -717,7 +713,25 @@
         public String getSystemDialerPackage() {
             try {
                 Log.startSession("TSI.gSDP");
-                return TelecomServiceImpl.getSystemDialerPackage(mContext);
+                return mDefaultDialerCache.getSystemDialerApplication();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        public void setSystemDialer(ComponentName testComponentName) {
+            try {
+                Log.startSession("TSI.sSD");
+                enforceModifyPermission();
+                enforceShellOnly(Binder.getCallingUid(), "setSystemDialer");
+                synchronized (mLock) {
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        mDefaultDialerCache.setSystemDialerComponentName(testComponentName);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
             } finally {
                 Log.endSession();
             }
@@ -929,21 +943,19 @@
         public boolean handlePinMmi(String dialString, String callingPackage) {
             try {
                 Log.startSession("TSI.hPM");
-                synchronized (mLock) {
-                    enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
+                enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
 
-                    // Switch identity so that TelephonyManager checks Telecom's permissions
-                    // instead.
-                    long token = Binder.clearCallingIdentity();
-                    boolean retval = false;
-                    try {
-                        retval = getTelephonyManager().handlePinMmi(dialString);
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
-                    }
-
-                    return retval;
+                // Switch identity so that TelephonyManager checks Telecom's permissions
+                // instead.
+                long token = Binder.clearCallingIdentity();
+                boolean retval = false;
+                try {
+                    retval = getTelephonyManager().handlePinMmi(dialString);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
                 }
+
+                return retval;
             }finally {
                 Log.endSession();
             }
@@ -957,29 +969,33 @@
                 String dialString, String callingPackage) {
             try {
                 Log.startSession("TSI.hPMFPA");
-                synchronized (mLock) {
-                    enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
 
-                    UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
+                UserHandle callingUserHandle = Binder.getCallingUserHandle();
+                synchronized (mLock) {
                     if (!isPhoneAccountHandleVisibleToCallingUser(accountHandle,
                             callingUserHandle)) {
-                        Log.d(this, "%s is not visible for the calling user [hMMI]", accountHandle);
+                        Log.d(this, "%s is not visible for the calling user [hMMI]",
+                                accountHandle);
                         return false;
                     }
-
-                    // Switch identity so that TelephonyManager checks Telecom's permissions
-                    // instead.
-                    long token = Binder.clearCallingIdentity();
-                    boolean retval = false;
-                    try {
-                        int subId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(
-                                accountHandle);
-                        retval = getTelephonyManager().handlePinMmiForSubscriber(subId, dialString);
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
-                    }
-                    return retval;
                 }
+
+                // Switch identity so that TelephonyManager checks Telecom's permissions
+                // instead.
+                long token = Binder.clearCallingIdentity();
+                boolean retval = false;
+                int subId;
+                try {
+                    synchronized (mLock) {
+                        subId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(
+                                accountHandle);
+                    }
+                    retval = getTelephonyManager().handlePinMmiForSubscriber(subId, dialString);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+                return retval;
             }finally {
                 Log.endSession();
             }
@@ -993,28 +1009,28 @@
                 String callingPackage) {
             try {
                 Log.startSession("TSI.aAUFPA");
+                enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
                 synchronized (mLock) {
-                    enforcePermissionOrPrivilegedDialer(MODIFY_PHONE_STATE, callingPackage);
                     if (!isPhoneAccountHandleVisibleToCallingUser(accountHandle,
                             Binder.getCallingUserHandle())) {
                         Log.d(this, "%s is not visible for the calling user [gA4PA]",
                                 accountHandle);
                         return null;
                     }
-                    // Switch identity so that TelephonyManager checks Telecom's permissions
-                    // instead.
-                    long token = Binder.clearCallingIdentity();
-                    String retval = "content://icc/adn/";
-                    try {
-                        long subId = mPhoneAccountRegistrar
-                                .getSubscriptionIdForPhoneAccount(accountHandle);
-                        retval = retval + "subId/" + subId;
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
-                    }
-
-                    return Uri.parse(retval);
                 }
+                // Switch identity so that TelephonyManager checks Telecom's permissions
+                // instead.
+                long token = Binder.clearCallingIdentity();
+                String retval = "content://icc/adn/";
+                try {
+                    long subId = mPhoneAccountRegistrar
+                            .getSubscriptionIdForPhoneAccount(accountHandle);
+                    retval = retval + "subId/" + subId;
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+
+                return Uri.parse(retval);
             } finally {
                 Log.endSession();
             }
@@ -1376,10 +1392,13 @@
                 return;
             }
 
+
             if (args.length > 0 && Analytics.ANALYTICS_DUMPSYS_ARG.equals(args[0])) {
-                Analytics.dumpToEncodedProto(writer, args);
+                Binder.withCleanCallingIdentity(() ->
+                        Analytics.dumpToEncodedProto(mContext, writer, args));
                 return;
             }
+
             boolean isTimeLineView = (args.length > 0 && TIME_LINE_ARG.equalsIgnoreCase(args[0]));
 
             final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
@@ -1480,6 +1499,26 @@
             }
         }
 
+        @Override
+        public void setTestEmergencyPhoneAccountPackageNameFilter(String packageName) {
+            try {
+                Log.startSession("TSI.sTPAPNF");
+                enforceModifyPermission();
+                enforceShellOnly(Binder.getCallingUid(),
+                        "setTestEmergencyPhoneAccountPackageNameFilter");
+                synchronized (mLock) {
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        mPhoneAccountRegistrar.setTestPhoneAccountPackageNameFilter(packageName);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+            } finally {
+                Log.endSession();
+            }
+        }
+
         /**
          * See {@link TelecomManager#isInEmergencyCall()}
          */
@@ -1504,10 +1543,10 @@
         }
 
         /**
-         * See {@link TelecomManager#handleCallIntent(Intent)} ()}
+         * See {@link TelecomManager#handleCallIntent(Intent, String)}
          */
         @Override
-        public void handleCallIntent(Intent intent) {
+        public void handleCallIntent(Intent intent, String callingPackage) {
             try {
                 Log.startSession("TSI.hCI");
                 synchronized (mLock) {
@@ -1518,7 +1557,7 @@
                     try {
                         Log.i(this, "handleCallIntent: handling call intent");
                         mCallIntentProcessorAdapter.processOutgoingCallIntent(mContext,
-                                mCallsManager, intent, null /* callingPackage */);
+                                mCallsManager, intent, callingPackage);
                     } finally {
                         Binder.restoreCallingIdentity(token);
                     }
@@ -1743,10 +1782,6 @@
         });
     }
 
-    public static String getSystemDialerPackage(Context context) {
-        return context.getResources().getString(com.android.internal.R.string.config_defaultDialer);
-    }
-
     public ITelecomService.Stub getBinder() {
         return mBinderImpl;
     }
@@ -1757,7 +1792,10 @@
 
     private boolean isPhoneAccountHandleVisibleToCallingUser(
             PhoneAccountHandle phoneAccountUserHandle, UserHandle callingUser) {
-        return mPhoneAccountRegistrar.getPhoneAccount(phoneAccountUserHandle, callingUser) != null;
+        synchronized (mLock) {
+            return mPhoneAccountRegistrar.getPhoneAccount(phoneAccountUserHandle, callingUser)
+                    != null;
+        }
     }
 
     private boolean isCallerSystemApp() {
@@ -1932,6 +1970,15 @@
         }
     }
 
+    // to be used for TestApi methods that can only be called with SHELL UID.
+    private void enforceShellOnly(int callingUid, String message) {
+        if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+            return; // okay
+        }
+
+        throw new SecurityException(message + ": Only shell user can call it");
+    }
+
     private boolean canReadPhoneState(String callingPackage, String message) {
         // The system/default dialer can always read phone state - so that emergency calls will
         // still work.
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index e772eb4..f354b94 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -20,6 +20,7 @@
 import com.android.server.telecom.bluetooth.BluetoothDeviceManager;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
 import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
+import com.android.server.telecom.callfiltering.IncomingCallFilter;
 import com.android.server.telecom.components.UserCallIntentProcessor;
 import com.android.server.telecom.components.UserCallIntentProcessorFactory;
 import com.android.server.telecom.ui.IncomingCallNotifier;
@@ -197,8 +198,11 @@
             IncomingCallNotifier incomingCallNotifier,
             InCallTonePlayer.ToneGeneratorFactory toneGeneratorFactory,
             CallAudioRouteStateMachine.Factory callAudioRouteStateMachineFactory,
+            CallAudioModeStateMachine.Factory callAudioModeStateMachineFactory,
             ClockProxy clockProxy,
-            RoleManagerAdapter roleManagerAdapter) {
+            RoleManagerAdapter roleManagerAdapter,
+            IncomingCallFilter.Factory incomingCallFilterFactory,
+            ContactsAsyncHelper.Factory contactsAsyncHelperFactory) {
         mContext = context.getApplicationContext();
         LogUtils.initLogging(mContext);
         DefaultDialerManagerAdapter defaultDialerAdapter =
@@ -223,7 +227,7 @@
                         return null;
                     }
                 });
-        mContactsAsyncHelper = new ContactsAsyncHelper(
+        mContactsAsyncHelper = contactsAsyncHelperFactory.create(
                 new ContactsAsyncHelper.ContentResolverAdapter() {
                     @Override
                     public InputStream openInputStream(Context context, Uri uri)
@@ -250,7 +254,7 @@
                         mContactsAsyncHelper, mLock);
 
         EmergencyCallHelper emergencyCallHelper = new EmergencyCallHelper(mContext,
-                TelecomServiceImpl.getSystemDialerPackage(mContext), timeoutsAdapter);
+                defaultDialerCache, timeoutsAdapter);
 
         InCallControllerFactory inCallControllerFactory = new InCallControllerFactory() {
             @Override
@@ -286,9 +290,10 @@
                 clockProxy,
                 bluetoothStateReceiver,
                 callAudioRouteStateMachineFactory,
-                new CallAudioModeStateMachine.Factory(),
+                callAudioModeStateMachineFactory,
                 inCallControllerFactory,
-                roleManagerAdapter);
+                roleManagerAdapter,
+                incomingCallFilterFactory);
 
         mIncomingCallNotifier = incomingCallNotifier;
         incomingCallNotifier.setCallsManagerProxy(new IncomingCallNotifier.CallsManagerProxy() {
@@ -322,7 +327,7 @@
 
         mBluetoothPhoneServiceImpl = bluetoothPhoneServiceImplFactory.makeBluetoothPhoneServiceImpl(
                 mContext, mLock, mCallsManager, mPhoneAccountRegistrar);
-        mCallIntentProcessor = new CallIntentProcessor(mContext, mCallsManager);
+        mCallIntentProcessor = new CallIntentProcessor(mContext, mCallsManager, defaultDialerCache);
         mTelecomBroadcastIntentProcessor = new TelecomBroadcastIntentProcessor(
                 mContext, mCallsManager);
 
@@ -335,7 +340,7 @@
         final UserManager userManager = UserManager.get(mContext);
         mTelecomServiceImpl = new TelecomServiceImpl(
                 mContext, mCallsManager, mPhoneAccountRegistrar,
-                new CallIntentProcessor.AdapterImpl(),
+                new CallIntentProcessor.AdapterImpl(defaultDialerCache),
                 new UserCallIntentProcessorFactory() {
                     @Override
                     public UserCallIntentProcessor create(Context context, UserHandle userHandle) {
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
index 23a086b..92de536 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
@@ -22,7 +22,9 @@
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.telecom.Log;
+import android.util.LocalLog;
 
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.telecom.BluetoothAdapterProxy;
 import com.android.server.telecom.BluetoothHeadsetProxy;
 
@@ -43,18 +45,21 @@
                     Log.startSession("BMSL.oSC");
                     try {
                         synchronized (mLock) {
+                            String logString;
                             if (profile == BluetoothProfile.HEADSET) {
                                 mBluetoothHeadsetService =
                                         new BluetoothHeadsetProxy((BluetoothHeadset) proxy);
-                                Log.i(this, "- Got BluetoothHeadset: " + mBluetoothHeadsetService);
+                                logString = "Got BluetoothHeadset: " + mBluetoothHeadsetService;
                             } else if (profile == BluetoothProfile.HEARING_AID) {
                                 mBluetoothHearingAidService = (BluetoothHearingAid) proxy;
-                                Log.i(this, "- Got BluetoothHearingAid: "
-                                        + mBluetoothHearingAidService);
+                                logString = "Got BluetoothHearingAid: "
+                                        + mBluetoothHearingAidService;
                             } else {
-                                Log.w(this, "Connected to non-requested bluetooth service." +
-                                        " Not changing bluetooth headset.");
+                                logString = "Connected to non-requested bluetooth service." +
+                                        " Not changing bluetooth headset.";
                             }
+                            Log.i(BluetoothDeviceManager.this, logString);
+                            mLocalLog.log(logString);
                         }
                     } finally {
                         Log.endSession();
@@ -67,23 +72,25 @@
                     try {
                         synchronized (mLock) {
                             LinkedHashMap<String, BluetoothDevice> lostServiceDevices;
+                            String logString;
                             if (profile == BluetoothProfile.HEADSET) {
                                 mBluetoothHeadsetService = null;
-                                Log.i(BluetoothDeviceManager.this,
-                                        "Lost BluetoothHeadset service. " +
-                                                "Removing all tracked devices.");
                                 lostServiceDevices = mHfpDevicesByAddress;
                                 mBluetoothRouteManager.onActiveDeviceChanged(null, false);
+                                logString = "Lost BluetoothHeadset service. " +
+                                        "Removing all tracked devices";
                             } else if (profile == BluetoothProfile.HEARING_AID) {
                                 mBluetoothHearingAidService = null;
-                                Log.i(BluetoothDeviceManager.this,
-                                        "Lost BluetoothHearingAid service. " +
-                                                "Removing all tracked devices.");
+                                logString = "Lost BluetoothHearingAid service. " +
+                                        "Removing all tracked devices.";
                                 lostServiceDevices = mHearingAidDevicesByAddress;
                                 mBluetoothRouteManager.onActiveDeviceChanged(null, true);
                             } else {
                                 return;
                             }
+                            Log.i(BluetoothDeviceManager.this, logString);
+                            mLocalLog.log(logString);
+
                             List<BluetoothDevice> devicesToRemove = new LinkedList<>(
                                     lostServiceDevices.values());
                             lostServiceDevices.clear();
@@ -103,6 +110,7 @@
             new LinkedHashMap<>();
     private final LinkedHashMap<BluetoothDevice, Long> mHearingAidDeviceSyncIds =
             new LinkedHashMap<>();
+    private final LocalLog mLocalLog = new LocalLog(20);
 
     // This lock only protects internal state -- it doesn't lock on anything going into Telecom.
     private final Object mLock = new Object();
@@ -188,6 +196,8 @@
     }
 
     void onDeviceConnected(BluetoothDevice device, boolean isHearingAid) {
+        mLocalLog.log("Device connected -- address: " + device.getAddress() + " isHeadingAid: "
+                + isHearingAid);
         synchronized (mLock) {
             LinkedHashMap<String, BluetoothDevice> targetDeviceMap;
             if (isHearingAid) {
@@ -213,6 +223,8 @@
     }
 
     void onDeviceDisconnected(BluetoothDevice device, boolean isHearingAid) {
+        mLocalLog.log("Device disconnected -- address: " + device.getAddress() + " isHeadingAid: "
+                + isHearingAid);
         synchronized (mLock) {
             LinkedHashMap<String, BluetoothDevice> targetDeviceMap;
             if (isHearingAid) {
@@ -297,4 +309,7 @@
         }
     }
 
+    public void dump(IndentingPrintWriter pw) {
+        mLocalLog.dump(pw);
+    }
 }
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
index 9e64b56..7671abd 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -152,6 +152,10 @@
         }
     }
 
+    public BluetoothDeviceManager getBluetoothDeviceManager() {
+        return mBluetoothDeviceManager;
+    }
+
     public BluetoothStateReceiver(BluetoothDeviceManager deviceManager,
             BluetoothRouteManager routeManager) {
         mBluetoothDeviceManager = deviceManager;
diff --git a/src/com/android/server/telecom/callfiltering/AsyncBlockCheckFilter.java b/src/com/android/server/telecom/callfiltering/AsyncBlockCheckFilter.java
index 0abd15d..edc68dd 100644
--- a/src/com/android/server/telecom/callfiltering/AsyncBlockCheckFilter.java
+++ b/src/com/android/server/telecom/callfiltering/AsyncBlockCheckFilter.java
@@ -30,6 +30,7 @@
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallerInfoLookupHelper;
 import com.android.server.telecom.LogUtils;
+import com.android.server.telecom.callfiltering.CallFilteringResult.Builder;
 import com.android.server.telecom.settings.BlockedNumbersUtil;
 
 /**
@@ -121,15 +122,15 @@
         try {
             CallFilteringResult result;
             if (isBlocked) {
-                result = new CallFilteringResult(
-                        false, // shouldAllowCall
-                        true, //shouldReject
-                        true, //shouldAddToCallLog
-                        false, // shouldShowNotification
-                        convertBlockStatusToReason(), //callBlockReason
-                        null, //callScreeningAppName
-                        null //callScreeningComponentName
-                );
+                result = new Builder()
+                        .setShouldAllowCall(false)
+                        .setShouldReject(true)
+                        .setShouldAddToCallLog(true)
+                        .setShouldShowNotification(false)
+                        .setCallBlockReason(convertBlockStatusToReason())
+                        .setCallScreeningAppName(null)
+                        .setCallScreeningComponentName(null)
+                        .build();
                 if (mCallBlockListener != null) {
                     String number = mIncomingCall.getHandle() == null ? null
                             : mIncomingCall.getHandle().getSchemeSpecificPart();
@@ -137,12 +138,12 @@
                             mIncomingCall.getInitiatingUser());
                 }
             } else {
-                result = new CallFilteringResult(
-                        true, // shouldAllowCall
-                        false, // shouldReject
-                        true, // shouldAddToCallLog
-                        true // shouldShowNotification
-                );
+                result = new Builder()
+                        .setShouldAllowCall(true)
+                        .setShouldReject(false)
+                        .setShouldAddToCallLog(true)
+                        .setShouldShowNotification(true)
+                        .build();
             }
             Log.addEvent(mIncomingCall, LogUtils.Events.BLOCK_CHECK_FINISHED,
                     BlockedNumberContract.SystemContract.blockStatusToString(mBlockStatus) + " "
diff --git a/src/com/android/server/telecom/callfiltering/CallFilteringResult.java b/src/com/android/server/telecom/callfiltering/CallFilteringResult.java
index af3ee1e..028fbf5 100644
--- a/src/com/android/server/telecom/callfiltering/CallFilteringResult.java
+++ b/src/com/android/server/telecom/callfiltering/CallFilteringResult.java
@@ -21,47 +21,73 @@
 import android.text.TextUtils;
 
 public class CallFilteringResult {
+    public static class Builder {
+        private boolean mShouldAllowCall;
+        private boolean mShouldReject;
+        private boolean mShouldAddToCallLog;
+        private boolean mShouldShowNotification;
+        private boolean mShouldSilence = false;
+        private int mCallBlockReason = Calls.BLOCK_REASON_NOT_BLOCKED;
+        private CharSequence mCallScreeningAppName = null;
+        private String mCallScreeningComponentName = null;
+
+        public Builder setShouldAllowCall(boolean shouldAllowCall) {
+            mShouldAllowCall = shouldAllowCall;
+            return this;
+        }
+
+        public Builder setShouldReject(boolean shouldReject) {
+            mShouldReject = shouldReject;
+            return this;
+        }
+
+        public Builder setShouldAddToCallLog(boolean shouldAddToCallLog) {
+            mShouldAddToCallLog = shouldAddToCallLog;
+            return this;
+        }
+
+        public Builder setShouldShowNotification(boolean shouldShowNotification) {
+            mShouldShowNotification = shouldShowNotification;
+            return this;
+        }
+
+        public Builder setShouldSilence(boolean shouldSilence) {
+            mShouldSilence = shouldSilence;
+            return this;
+        }
+
+        public Builder setCallBlockReason(int callBlockReason) {
+            mCallBlockReason = callBlockReason;
+            return this;
+        }
+
+        public Builder setCallScreeningAppName(CharSequence callScreeningAppName) {
+            mCallScreeningAppName = callScreeningAppName;
+            return this;
+        }
+
+        public Builder setCallScreeningComponentName(String callScreeningComponentName) {
+            mCallScreeningComponentName = callScreeningComponentName;
+            return this;
+        }
+
+        public CallFilteringResult build() {
+            return new CallFilteringResult(mShouldAllowCall, mShouldReject, mShouldSilence,
+                    mShouldAddToCallLog, mShouldShowNotification, mCallBlockReason,
+                    mCallScreeningAppName, mCallScreeningComponentName);
+        }
+    }
+
     public boolean shouldAllowCall;
     public boolean shouldReject;
     public boolean shouldSilence;
     public boolean shouldAddToCallLog;
     public boolean shouldShowNotification;
-    public int mCallBlockReason = CallLog.Calls.BLOCK_REASON_NOT_BLOCKED;
-    public CharSequence mCallScreeningAppName = null;
-    public String mCallScreeningComponentName = null;
+    public int mCallBlockReason;
+    public CharSequence mCallScreeningAppName;
+    public String mCallScreeningComponentName;
 
-    public CallFilteringResult(boolean shouldAllowCall, boolean shouldReject, boolean
-            shouldAddToCallLog, boolean shouldShowNotification) {
-        this.shouldAllowCall = shouldAllowCall;
-        this.shouldReject = shouldReject;
-        this.shouldSilence = false;
-        this.shouldAddToCallLog = shouldAddToCallLog;
-        this.shouldShowNotification = shouldShowNotification;
-    }
-
-    public CallFilteringResult(boolean shouldAllowCall, boolean shouldReject, boolean
-            shouldAddToCallLog, boolean shouldShowNotification, int callBlockReason,
-            CharSequence callScreeningAppName, String callScreeningComponentName) {
-        this.shouldAllowCall = shouldAllowCall;
-        this.shouldReject = shouldReject;
-        this.shouldSilence = false;
-        this.shouldAddToCallLog = shouldAddToCallLog;
-        this.shouldShowNotification = shouldShowNotification;
-        this.mCallBlockReason = callBlockReason;
-        this.mCallScreeningAppName = callScreeningAppName;
-        this.mCallScreeningComponentName = callScreeningComponentName;
-    }
-
-    public CallFilteringResult(boolean shouldAllowCall, boolean shouldReject, boolean
-            shouldSilence, boolean shouldAddToCallLog, boolean shouldShowNotification) {
-        this.shouldAllowCall = shouldAllowCall;
-        this.shouldReject = shouldReject;
-        this.shouldSilence = shouldSilence;
-        this.shouldAddToCallLog = shouldAddToCallLog;
-        this.shouldShowNotification = shouldShowNotification;
-    }
-
-    public CallFilteringResult(boolean shouldAllowCall, boolean shouldReject, boolean
+    private CallFilteringResult(boolean shouldAllowCall, boolean shouldReject, boolean
             shouldSilence, boolean shouldAddToCallLog, boolean shouldShowNotification, int
             callBlockReason, CharSequence callScreeningAppName, String callScreeningComponentName) {
         this.shouldAllowCall = shouldAllowCall;
@@ -109,12 +135,13 @@
                 other.mCallScreeningAppName, other.mCallScreeningComponentName);
         }
 
-        return new CallFilteringResult(
-            shouldAllowCall && other.shouldAllowCall,
-            shouldReject || other.shouldReject,
-            shouldSilence || other.shouldSilence,
-            shouldAddToCallLog && other.shouldAddToCallLog,
-            shouldShowNotification && other.shouldShowNotification);
+        return new Builder()
+                .setShouldAllowCall(shouldAllowCall && other.shouldAllowCall)
+                .setShouldReject(shouldReject || other.shouldReject)
+                .setShouldSilence(shouldSilence || other.shouldSilence)
+                .setShouldAddToCallLog(shouldAddToCallLog && other.shouldAddToCallLog)
+                .setShouldShowNotification(shouldShowNotification && other.shouldShowNotification)
+                .build();
     }
 
     private boolean isBlockedByProvider(int blockReason) {
@@ -131,15 +158,16 @@
 
     private CallFilteringResult getCombinedCallFilteringResult(CallFilteringResult other,
         int callBlockReason, CharSequence callScreeningAppName, String callScreeningComponentName) {
-        return new CallFilteringResult(
-            shouldAllowCall && other.shouldAllowCall,
-            shouldReject || other.shouldReject,
-            shouldSilence|| other.shouldSilence,
-            shouldAddToCallLog && other.shouldAddToCallLog,
-            shouldShowNotification && other.shouldShowNotification,
-            callBlockReason,
-            callScreeningAppName,
-            callScreeningComponentName);
+        return new Builder()
+                .setShouldAllowCall(shouldAllowCall && other.shouldAllowCall)
+                .setShouldReject(shouldReject || other.shouldReject)
+                .setShouldSilence(shouldSilence || other.shouldSilence)
+                .setShouldAddToCallLog(shouldAddToCallLog && other.shouldAddToCallLog)
+                .setShouldShowNotification(shouldShowNotification && other.shouldShowNotification)
+                .setCallBlockReason(callBlockReason)
+                .setCallScreeningAppName(callScreeningAppName)
+                .setCallScreeningComponentName(callScreeningComponentName)
+                .build();
     }
 
 
diff --git a/src/com/android/server/telecom/callfiltering/CallScreeningServiceController.java b/src/com/android/server/telecom/callfiltering/CallScreeningServiceController.java
index 5b1971e..42d9a59 100644
--- a/src/com/android/server/telecom/callfiltering/CallScreeningServiceController.java
+++ b/src/com/android/server/telecom/callfiltering/CallScreeningServiceController.java
@@ -39,6 +39,7 @@
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.TelecomServiceImpl;
 import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.callfiltering.CallFilteringResult.Builder;
 
 /**
  * This class supports binding to the various {@link android.telecom.CallScreeningService}:
@@ -66,12 +67,12 @@
     private Call mCall;
     private CallFilterResultCallback mCallback;
 
-    private CallFilteringResult mResult = new CallFilteringResult(
-            true, // shouldAllowCall
-            false, // shouldReject
-            true, // shouldAddToCallLog
-            true // shouldShowNotification
-    );
+    private CallFilteringResult mResult = new Builder()
+            .setShouldAllowCall(true)
+            .setShouldReject(false)
+            .setShouldAddToCallLog(true)
+            .setShouldShowNotification(true)
+            .build();
 
     private boolean mIsFinished;
     private boolean mIsCarrierFinished;
@@ -195,10 +196,18 @@
             if (TextUtils.isEmpty(userChosenPackageName)) {
                 mIsUserChosenFinished = true;
             } else {
-                createCallScreeningServiceFilter().startCallScreeningFilter(mCall,
-                        CallScreeningServiceController.this, userChosenPackageName,
-                        mAppLabelProxy.getAppLabel(userChosenPackageName),
-                        CallScreeningServiceFilter.CALL_SCREENING_FILTER_TYPE_USER_SELECTED);
+                // If the user chosen call screening service is the same as the default dialer, then
+                // we have already bound to it above and don't need to do so again here.
+                if (userChosenPackageName.equals(dialerPackageName)) {
+                    Log.addEvent(mCall, LogUtils.Events.SCREENING_SKIPPED,
+                            "user pkg same as dialer: " + userChosenPackageName);
+                    mIsUserChosenFinished = true;
+                } else {
+                    createCallScreeningServiceFilter().startCallScreeningFilter(mCall,
+                            CallScreeningServiceController.this, userChosenPackageName,
+                            mAppLabelProxy.getAppLabel(userChosenPackageName),
+                            CallScreeningServiceFilter.CALL_SCREENING_FILTER_TYPE_USER_SELECTED);
+                }
             }
 
             if (mIsDefaultDialerFinished && mIsUserChosenFinished) {
@@ -253,7 +262,7 @@
         PersistableBundle configBundle = configManager.getConfig();
         if (configBundle != null) {
             componentName = ComponentName.unflattenFromString(configBundle.getString
-                    (CarrierConfigManager.KEY_CARRIER_CALL_SCREENING_APP_STRING));
+                    (CarrierConfigManager.KEY_CARRIER_CALL_SCREENING_APP_STRING, ""));
         }
 
         return componentName != null ? componentName.getPackageName() : null;
diff --git a/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java b/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
index c8ec18a..9ea8d70 100644
--- a/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
+++ b/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
@@ -24,7 +24,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.provider.CallLog;
+import android.provider.CallLog.Calls;
 import android.provider.Settings;
 import android.telecom.Log;
 import android.telecom.TelecomManager;
@@ -41,6 +41,7 @@
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.TelecomServiceImpl.SettingsSecureAdapter;
 import com.android.server.telecom.TelecomSystem;
+import com.android.server.telecom.callfiltering.CallFilteringResult.Builder;
 
 /**
  * Binds to {@link ICallScreeningService} to allow call blocking. A single instance of this class
@@ -96,13 +97,13 @@
                 synchronized (mTelecomLock) {
                     Log.d(this, "allowCall(%s)", callId);
                     if (mCall != null && mCall.getId().equals(callId)) {
-                        mResult = new CallFilteringResult(
-                                true, // shouldAllowCall
-                                false, //shouldReject
-                                false, //shouldSilence
-                                true, //shouldAddToCallLog
-                                true // shouldShowNotification
-                        );
+                        mResult = new Builder()
+                                .setShouldAllowCall(true)
+                                .setShouldReject(false)
+                                .setShouldSilence(false)
+                                .setShouldAddToCallLog(true)
+                                .setShouldShowNotification(true)
+                                .build();
                     } else {
                         Log.w(this, "allowCall, unknown call id: %s", callId);
                     }
@@ -131,16 +132,16 @@
                                     + "shouldShowNotification: %b", callId, shouldReject,
                             isServiceRequestingLogging, shouldShowNotification);
                     if (mCall != null && mCall.getId().equals(callId)) {
-                        mResult = new CallFilteringResult(
-                                false, // shouldAllowCall
-                                shouldReject, //shouldReject
-                                false, // shouldSilenceCall
-                                isServiceRequestingLogging, //shouldAddToCallLog
-                                shouldShowNotification, // shouldShowNotification
-                                CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE, //callBlockReason
-                                mAppName, //callScreeningAppName
-                                componentName.flattenToString() //callScreeningComponentName
-                        );
+                        mResult = new Builder()
+                                .setShouldAllowCall(false)
+                                .setShouldReject(shouldReject)
+                                .setShouldSilence(false)
+                                .setShouldAddToCallLog(isServiceRequestingLogging)
+                                .setShouldShowNotification(shouldShowNotification)
+                                .setCallBlockReason(Calls.BLOCK_REASON_CALL_SCREENING_SERVICE)
+                                .setCallScreeningAppName(mAppName)
+                                .setCallScreeningComponentName(componentName.flattenToString())
+                                .build();
                     } else {
                         Log.w(this, "disallowCall, unknown call id: %s", callId);
                     }
@@ -160,13 +161,13 @@
                 synchronized (mTelecomLock) {
                     Log.d(this, "silenceCall(%s)", callId);
                     if (mCall != null && mCall.getId().equals(callId)) {
-                        mResult = new CallFilteringResult(
-                                true, // shouldAllowCall
-                                false, //shouldReject
-                                true, //shouldSilence
-                                true, //shouldAddToCallLog
-                                true // shouldShowNotification
-                        );
+                        mResult = new Builder()
+                                .setShouldAllowCall(true)
+                                .setShouldReject(false)
+                                .setShouldSilence(true)
+                                .setShouldAddToCallLog(true)
+                                .setShouldShowNotification(true)
+                                .build();
                     } else {
                         Log.w(this, "silenceCall, unknown call id: %s", callId);
                     }
@@ -177,6 +178,11 @@
                 Log.endSession();
             }
         }
+
+        @Override
+        public void screenCallFurther(String callId) {
+            // TODO: implement this
+        }
     }
 
     private final Context mContext;
@@ -194,12 +200,12 @@
     private boolean mHasFinished = false;
     private int mCallScreeningServiceType;
 
-    private CallFilteringResult mResult = new CallFilteringResult(
-            true, // shouldAllowCall
-            false, //shouldReject
-            true, //shouldAddToCallLog
-            true // shouldShowNotification
-    );
+    private CallFilteringResult mResult = new Builder()
+            .setShouldAllowCall(true)
+            .setShouldReject(false)
+            .setShouldAddToCallLog(true)
+            .setShouldShowNotification(true)
+            .build();
 
     public CallScreeningServiceFilter(
             Context context,
diff --git a/src/com/android/server/telecom/callfiltering/DirectToVoicemailCallFilter.java b/src/com/android/server/telecom/callfiltering/DirectToVoicemailCallFilter.java
index 3a8ff7d..16d731b 100644
--- a/src/com/android/server/telecom/callfiltering/DirectToVoicemailCallFilter.java
+++ b/src/com/android/server/telecom/callfiltering/DirectToVoicemailCallFilter.java
@@ -17,13 +17,14 @@
 package com.android.server.telecom.callfiltering;
 
 import android.net.Uri;
-import android.provider.CallLog;
+import android.provider.CallLog.Calls;
 import android.telecom.Log;
 
 import com.android.internal.telephony.CallerInfo;
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallerInfoLookupHelper;
 import com.android.server.telecom.LogUtils;
+import com.android.server.telecom.callfiltering.CallFilteringResult.Builder;
 
 import java.util.Objects;
 
@@ -46,33 +47,32 @@
                         CallFilteringResult result;
                         if ((handle != null) && Objects.equals(callHandle, handle)) {
                             if (info != null && info.shouldSendToVoicemail) {
-                                result = new CallFilteringResult(
-                                        false, // shouldAllowCall
-                                        true, // shouldReject
-                                        true, // shouldAddToCallLog
-                                        true, // shouldShowNotification
-                                        CallLog.Calls.BLOCK_REASON_DIRECT_TO_VOICEMAIL,
-                                        //callBlockReason
-                                        null, //callScreeningAppName
-                                        null // callScreeningComponentName
-                                );
+                                result = new Builder()
+                                        .setShouldAllowCall(false)
+                                        .setShouldReject(true)
+                                        .setShouldAddToCallLog(true)
+                                        .setShouldShowNotification(true)
+                                        .setCallBlockReason(Calls.BLOCK_REASON_DIRECT_TO_VOICEMAIL)
+                                        .setCallScreeningAppName(null)
+                                        .setCallScreeningComponentName(null)
+                                        .build();
                             } else {
-                                result = new CallFilteringResult(
-                                        true, // shouldAllowCall
-                                        false, // shouldReject
-                                        true, // shouldAddToCallLog
-                                        true // shouldShowNotification
-                                );
+                                result = new Builder()
+                                        .setShouldAllowCall(true)
+                                        .setShouldReject(false)
+                                        .setShouldAddToCallLog(true)
+                                        .setShouldShowNotification(true)
+                                        .build();
                             }
                             Log.addEvent(call, LogUtils.Events.DIRECT_TO_VM_FINISHED, result);
                             callback.onCallFilteringComplete(call, result);
                         } else {
-                            result = new CallFilteringResult(
-                                true, // shouldAllowCall
-                                false, // shouldReject
-                                true, // shouldAddToCallLog
-                                true // shouldShowNotification
-                            );
+                            result = new Builder()
+                                    .setShouldAllowCall(true)
+                                    .setShouldReject(false)
+                                    .setShouldAddToCallLog(true)
+                                    .setShouldShowNotification(true)
+                                    .build();
                             Log.addEvent(call, LogUtils.Events.DIRECT_TO_VM_FINISHED, result);
                             Log.w(this, "CallerInfo lookup returned with a different handle than " +
                                     "what was passed in. Was %s, should be %s", handle, callHandle);
diff --git a/src/com/android/server/telecom/callfiltering/IncomingCallFilter.java b/src/com/android/server/telecom/callfiltering/IncomingCallFilter.java
index 1c947d1..860de1f 100644
--- a/src/com/android/server/telecom/callfiltering/IncomingCallFilter.java
+++ b/src/com/android/server/telecom/callfiltering/IncomingCallFilter.java
@@ -27,36 +27,46 @@
 import com.android.server.telecom.LogUtils;
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.Timeouts;
+import com.android.server.telecom.callfiltering.CallFilteringResult.Builder;
 
 import java.util.List;
 
 public class IncomingCallFilter implements CallFilterResultCallback {
 
+    public static class Factory {
+        public IncomingCallFilter create(Context context, CallFilterResultCallback listener,
+                Call call, TelecomSystem.SyncRoot lock, Timeouts.Adapter timeoutsAdapter,
+                List<CallFilter> filters) {
+            return new IncomingCallFilter(context, listener, call, lock, timeoutsAdapter, filters,
+                    new Handler(Looper.getMainLooper()));
+        }
+    }
+
     public interface CallFilter {
         void startFilterLookup(Call call, CallFilterResultCallback listener);
     }
 
     private final TelecomSystem.SyncRoot mTelecomLock;
     private final Context mContext;
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final Handler mHandler;
     private final List<CallFilter> mFilters;
     private final Call mCall;
     private final CallFilterResultCallback mListener;
     private final Timeouts.Adapter mTimeoutsAdapter;
 
-    private CallFilteringResult mResult = new CallFilteringResult(
-            true, // shouldAllowCall
-            false, // shouldReject
-            true, // shouldAddToCallLog
-            true // shouldShowNotification
-    );
+    private CallFilteringResult mResult = new Builder()
+            .setShouldAllowCall(true)
+            .setShouldReject(false)
+            .setShouldAddToCallLog(true)
+            .setShouldShowNotification(true)
+            .build();
 
     private boolean mIsPending = true;
     private int mNumPendingFilters;
 
     public IncomingCallFilter(Context context, CallFilterResultCallback listener, Call call,
             TelecomSystem.SyncRoot lock, Timeouts.Adapter timeoutsAdapter,
-            List<CallFilter> filters) {
+            List<CallFilter> filters, Handler handler) {
         mContext = context;
         mListener = listener;
         mCall = call;
@@ -64,6 +74,7 @@
         mFilters = filters;
         mNumPendingFilters = filters.size();
         mTimeoutsAdapter = timeoutsAdapter;
+        mHandler = handler;
     }
 
     public void performFiltering() {
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index 956274a..0eab83f 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -24,7 +24,6 @@
 import android.media.IAudioService;
 import android.media.ToneGenerator;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.PowerManager;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -34,15 +33,16 @@
 import com.android.server.telecom.AsyncRingtonePlayer;
 import com.android.server.telecom.BluetoothAdapterProxy;
 import com.android.server.telecom.BluetoothPhoneServiceImpl;
+import com.android.server.telecom.CallAudioModeStateMachine;
 import com.android.server.telecom.CallAudioRouteStateMachine;
 import com.android.server.telecom.CallerInfoAsyncQueryFactory;
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.ClockProxy;
 import com.android.server.telecom.ConnectionServiceFocusManager;
+import com.android.server.telecom.ContactsAsyncHelper;
 import com.android.server.telecom.DefaultDialerCache;
 import com.android.server.telecom.HeadsetMediaButton;
 import com.android.server.telecom.HeadsetMediaButtonFactory;
-import com.android.server.telecom.InCallTonePlayer;
 import com.android.server.telecom.InCallWakeLockControllerFactory;
 import com.android.server.telecom.CallAudioManager;
 import com.android.server.telecom.PhoneAccountRegistrar;
@@ -51,11 +51,11 @@
 import com.android.server.telecom.InCallWakeLockController;
 import com.android.server.telecom.ProximitySensorManager;
 import com.android.server.telecom.R;
-import com.android.server.telecom.RoleManagerAdapter;
 import com.android.server.telecom.RoleManagerAdapterImpl;
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.TelecomWakeLock;
 import com.android.server.telecom.Timeouts;
+import com.android.server.telecom.callfiltering.IncomingCallFilter;
 import com.android.server.telecom.ui.IncomingCallNotifier;
 import com.android.server.telecom.ui.MissedCallNotifierImpl;
 import com.android.server.telecom.ui.NotificationChannelManager;
@@ -178,6 +178,7 @@
                             new IncomingCallNotifier(context),
                             ToneGenerator::new,
                             new CallAudioRouteStateMachine.Factory(),
+                            new CallAudioModeStateMachine.Factory(),
                             new ClockProxy() {
                                 @Override
                                 public long currentTimeMillis() {
@@ -190,7 +191,9 @@
                                 }
                             },
                             new RoleManagerAdapterImpl(context,
-                                    (RoleManager) context.getSystemService(Context.ROLE_SERVICE))));
+                                    (RoleManager) context.getSystemService(Context.ROLE_SERVICE)),
+                            new IncomingCallFilter.Factory(),
+                            new ContactsAsyncHelper.Factory()));
         }
         if (BluetoothAdapter.getDefaultAdapter() != null) {
             context.startService(new Intent(context, BluetoothPhoneService.class));
diff --git a/src/com/android/server/telecom/components/UserCallIntentProcessor.java b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
index ae4a7d8..134db80 100644
--- a/src/com/android/server/telecom/components/UserCallIntentProcessor.java
+++ b/src/com/android/server/telecom/components/UserCallIntentProcessor.java
@@ -22,13 +22,11 @@
 import android.net.Uri;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.telecom.DefaultDialerManager;
 import android.telecom.Log;
 import android.telecom.PhoneAccount;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
 
 import com.android.server.telecom.CallIntentProcessor;
 import com.android.server.telecom.R;
@@ -153,31 +151,12 @@
                 VideoProfile.STATE_AUDIO_ONLY);
         Log.d(this, "processOutgoingCallIntent videoState = " + videoState);
 
-        intent.putExtra(CallIntentProcessor.KEY_IS_PRIVILEGED_DIALER,
-                isDefaultOrSystemDialer(callingPackageName));
-
         // Save the user handle of current user before forwarding the intent to primary user.
         intent.putExtra(CallIntentProcessor.KEY_INITIATING_USER, mUserHandle);
 
         sendIntentToDestination(intent, isLocalInvocation, callingPackageName);
     }
 
-    private boolean isDefaultOrSystemDialer(String callingPackageName) {
-        if (TextUtils.isEmpty(callingPackageName)) {
-            return false;
-        }
-
-        final String defaultDialer = DefaultDialerManager.getDefaultDialerApplication(mContext,
-                mUserHandle.getIdentifier());
-        if (TextUtils.equals(defaultDialer, callingPackageName)) {
-            return true;
-        }
-
-        final TelecomManager telecomManager =
-                (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
-        return TextUtils.equals(telecomManager.getSystemDialerPackage(), callingPackageName);
-    }
-
     /**
      * Returns whether the device is voice-capable (e.g. a phone vs a tablet).
      *
@@ -211,7 +190,7 @@
             // process; we need to trampoline to TelecomSystem in the system server process.
             Log.i(this, "sendIntentToDestination: trampoline to Telecom.");
             TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
-            tm.handleCallIntent(intent);
+            tm.handleCallIntent(intent, callingPackage);
         }
         return true;
     }
diff --git a/testapps/res/layout/testdialer_main.xml b/testapps/res/layout/testdialer_main.xml
index 9da3789..a9507c3 100644
--- a/testapps/res/layout/testdialer_main.xml
+++ b/testapps/res/layout/testdialer_main.xml
@@ -59,4 +59,14 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="@string/startCallWithRtt"/>
+    <Button
+        android:id="@+id/toggle_car_mode"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Toggle Car mode" />
+    <Button
+        android:id="@+id/toggle_incallservice"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Toggle InCallService" />
 </LinearLayout>
diff --git a/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java b/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
index c7eccf7..9aa076f 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestDialerActivity.java
@@ -1,9 +1,14 @@
 package com.android.server.telecom.testapps;
 
+import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
+
 import android.app.Activity;
+import android.app.UiModeManager;
+import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.CallLog.Calls;
@@ -56,6 +61,18 @@
 
         mNumberView = (EditText) findViewById(R.id.number);
         mRttCheckbox = (CheckBox) findViewById(R.id.call_with_rtt_checkbox);
+        findViewById(R.id.toggle_car_mode).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                toggleCarMode();
+            }
+        });
+        findViewById(R.id.toggle_incallservice).setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                toggleInCallService();
+            }
+        });
         updateMutableUi();
     }
 
@@ -140,4 +157,29 @@
         Log.i("Santos xtr", intentExtras.toString());
         return intentExtras;
     }
+
+    private void toggleCarMode() {
+        UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE);
+        boolean isCarMode = uiModeManager.getCurrentModeType() == UI_MODE_TYPE_CAR;
+        if (isCarMode) {
+            uiModeManager.disableCarMode(0);
+        } else {
+            uiModeManager.enableCarMode(0);
+        }
+    }
+
+    private void toggleInCallService() {
+        ComponentName uiComponent = new ComponentName(
+                TestInCallServiceImpl.class.getPackage().getName(),
+                TestInCallServiceImpl.class.getName());
+        boolean isEnabled = getPackageManager().getComponentEnabledSetting(uiComponent)
+                == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+        getPackageManager().setComponentEnabledSetting(uiComponent,
+                isEnabled ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+                        : PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                PackageManager.DONT_KILL_APP);
+        isEnabled = getPackageManager().getComponentEnabledSetting(uiComponent)
+                == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+        Toast.makeText(this, "Is UI enabled? " + isEnabled, Toast.LENGTH_LONG).show();
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/AnalyticsTests.java b/tests/src/com/android/server/telecom/tests/AnalyticsTests.java
index 1fad3f7..ec5f7ba 100644
--- a/tests/src/com/android/server/telecom/tests/AnalyticsTests.java
+++ b/tests/src/com/android/server/telecom/tests/AnalyticsTests.java
@@ -25,6 +25,7 @@
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.os.Build;
@@ -39,6 +40,8 @@
 import android.telecom.TelecomManager;
 import android.telecom.VideoCallImpl;
 import android.telecom.VideoProfile;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Base64;
@@ -60,6 +63,7 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -70,10 +74,16 @@
 
 @RunWith(JUnit4.class)
 public class AnalyticsTests extends TelecomSystemTest {
+    private SubscriptionManager mSubscriptionManager;
+
     @Override
     @Before
     public void setUp() throws Exception {
         super.setUp();
+        // this is a mock
+        mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
+        when(mSubscriptionManager.getActiveSubscriptionInfoList())
+                .thenReturn(Collections.emptyList());
     }
 
     @Override
@@ -253,7 +263,7 @@
 
         StringWriter sw = new StringWriter();
         PrintWriter pw = new PrintWriter(sw);
-        Analytics.dumpToEncodedProto(pw, new String[]{});
+        Analytics.dumpToEncodedProto(mContext, pw, new String[]{});
         TelecomLogClass.TelecomLog analyticsProto =
                 TelecomLogClass.TelecomLog.parseFrom(Base64.decode(sw.toString(), Base64.DEFAULT));
 
@@ -300,6 +310,7 @@
     @Test
     public void testAnalyticsDumpToProto() throws Exception {
         Analytics.reset();
+        setupCarrierIds();
         IdPair testCall = startAndMakeActiveIncomingCall(
                 "650-555-1212",
                 mPhoneAccountA0.getAccountHandle(),
@@ -311,10 +322,11 @@
 
         StringWriter sw = new StringWriter();
         PrintWriter pw = new PrintWriter(sw);
-        Analytics.dumpToEncodedProto(pw, new String[]{});
+        Analytics.dumpToEncodedProto(mContext, pw, new String[]{});
         TelecomLogClass.TelecomLog analyticsProto =
                 TelecomLogClass.TelecomLog.parseFrom(Base64.decode(sw.toString(), Base64.DEFAULT));
 
+        assertEquals(1, analyticsProto.getCarrierId());
         assertEquals(1, analyticsProto.callLogs.length);
         TelecomLogClass.CallLog callLog = analyticsProto.callLogs[0];
 
@@ -413,7 +425,7 @@
 
         StringWriter sw = new StringWriter();
         PrintWriter pw = new PrintWriter(sw);
-        Analytics.dumpToEncodedProto(pw, new String[]{});
+        Analytics.dumpToEncodedProto(mContext, pw, new String[]{});
         TelecomLogClass.TelecomLog analyticsProto =
                 TelecomLogClass.TelecomLog.parseFrom(Base64.decode(sw.toString(), Base64.DEFAULT));
 
@@ -432,7 +444,7 @@
 
         StringWriter sw = new StringWriter();
         PrintWriter pw = new PrintWriter(sw);
-        Analytics.dumpToEncodedProto(pw, new String[]{});
+        Analytics.dumpToEncodedProto(mContext, pw, new String[]{});
         TelecomLogClass.TelecomLog analyticsProto =
                 TelecomLogClass.TelecomLog.parseFrom(Base64.decode(sw.toString(), Base64.DEFAULT));
 
@@ -445,4 +457,17 @@
     private void assertIsRoundedToOneSigFig(long x) {
         assertEquals(x, Analytics.roundToOneSigFig(x));
     }
+
+    private void setupCarrierIds() {
+        SubscriptionInfo subInfo1 = mock(SubscriptionInfo.class);
+        SubscriptionInfo subInfo2 = mock(SubscriptionInfo.class);
+        when(subInfo1.getCarrierId()).thenReturn(1);
+        when(subInfo2.getCarrierId()).thenReturn(2);
+        when(subInfo1.isOpportunistic()).thenReturn(false);
+        when(subInfo2.isOpportunistic()).thenReturn(true);
+        when(subInfo1.getSimSlotIndex()).thenReturn(0);
+        when(subInfo2.getSimSlotIndex()).thenReturn(1);
+        when(mSubscriptionManager.getActiveSubscriptionInfoList())
+                .thenReturn(Arrays.asList(subInfo2, subInfo1));
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/AsyncBlockCheckFilterTest.java b/tests/src/com/android/server/telecom/tests/AsyncBlockCheckFilterTest.java
index 91ddf5a..ee34b0c 100644
--- a/tests/src/com/android/server/telecom/tests/AsyncBlockCheckFilterTest.java
+++ b/tests/src/com/android/server/telecom/tests/AsyncBlockCheckFilterTest.java
@@ -23,7 +23,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.PersistableBundle;
-import android.provider.CallLog;
+import android.provider.CallLog.Calls;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -34,7 +34,9 @@
 import com.android.server.telecom.callfiltering.BlockCheckerAdapter;
 import com.android.server.telecom.callfiltering.CallFilterResultCallback;
 import com.android.server.telecom.callfiltering.CallFilteringResult;
+import com.android.server.telecom.callfiltering.CallFilteringResult.Builder;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -60,23 +62,22 @@
     @Mock private CarrierConfigManager mCarrierConfigManager;
 
     private AsyncBlockCheckFilter mFilter;
-    private static final CallFilteringResult BLOCK_RESULT = new CallFilteringResult(
-            false, // shouldAllowCall
-            true, //shouldReject
-            true, //shouldAddToCallLog
-            false, // shouldShowNotification
-            CallLog.Calls.BLOCK_REASON_BLOCKED_NUMBER, //blockReason
-            null, // callScreeningAppName
-            null //callScreeningComponentName
+    private static final CallFilteringResult BLOCK_RESULT = new Builder()
+            .setShouldAllowCall(false)
+            .setShouldReject(true)
+            .setShouldAddToCallLog(true)
+            .setShouldShowNotification(false)
+            .setCallBlockReason(Calls.BLOCK_REASON_BLOCKED_NUMBER)
+            .setCallScreeningAppName(null)
+            .setCallScreeningComponentName(null)
+            .build();
 
-    );
-
-    private static final CallFilteringResult PASS_RESULT = new CallFilteringResult(
-            true, // shouldAllowCall
-            false, // shouldReject
-            true, // shouldAddToCallLog
-            true // shouldShowNotification
-    );
+    private static final CallFilteringResult PASS_RESULT = new Builder()
+            .setShouldAllowCall(true)
+            .setShouldReject(false)
+            .setShouldAddToCallLog(true)
+            .setShouldShowNotification(true)
+            .build();
 
     private static final Uri TEST_HANDLE = Uri.parse("tel:1235551234");
     private static final int TEST_TIMEOUT = 1000;
@@ -90,6 +91,12 @@
                 mCallerInfoLookupHelper, null);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testBlockNumber() {
diff --git a/tests/src/com/android/server/telecom/tests/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
index 95ca3f3..04f0e66 100644
--- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
@@ -306,7 +306,8 @@
         mTelecomSystem.getTelecomServiceImpl().getBinder()
                 .addNewIncomingCall(mPhoneAccountA0.getAccountHandle(), extras);
 
-        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+                TEST_TIMEOUT);
         ArgumentCaptor<ConnectionRequest> connectionRequestCaptor
             = ArgumentCaptor.forClass(ConnectionRequest.class);
         verify(mConnectionServiceFixtureA.getTestDouble())
@@ -333,15 +334,18 @@
         String callId = startOutgoingPhoneCallWithNoPhoneAccount("650-555-1212",
                 mConnectionServiceFixtureA);
         mTelecomSystem.getCallsManager().getLatestPreAccountSelectionFuture().join();
-        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+                TEST_TIMEOUT);
         assertEquals(Call.STATE_SELECT_PHONE_ACCOUNT,
                 mInCallServiceFixtureX.getCall(callId).getState());
         assertEquals(Call.STATE_SELECT_PHONE_ACCOUNT,
                 mInCallServiceFixtureY.getCall(callId).getState());
         mInCallServiceFixtureX.mInCallAdapter.phoneAccountSelected(callId,
                 mPhoneAccountA0.getAccountHandle(), false);
-        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
-        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+                TEST_TIMEOUT);
+        waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+                TEST_TIMEOUT);
         verifyAndProcessOutgoingCallBroadcast(mPhoneAccountA0.getAccountHandle());
 
         IdPair ids = outgoingCallPhoneAccountSelected(mPhoneAccountA0.getAccountHandle(),
@@ -366,12 +370,14 @@
         mTelecomSystem.getTelecomServiceImpl().getBinder()
                 .addNewIncomingCall(mPhoneAccountA0.getAccountHandle(), extras);
 
-        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+                TEST_TIMEOUT);
         verify(mConnectionServiceFixtureA.getTestDouble())
                 .createConnection(any(PhoneAccountHandle.class), anyString(),
                         any(ConnectionRequest.class), eq(true), eq(false), any());
 
-        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+                TEST_TIMEOUT);
         assertEquals(1, mCallerInfoAsyncQueryFactoryFixture.mRequests.size());
         for (CallerInfoAsyncQueryFactoryFixture.Request request :
                 mCallerInfoAsyncQueryFactoryFixture.mRequests) {
@@ -411,12 +417,14 @@
         mTelecomSystem.getTelecomServiceImpl().getBinder()
                 .addNewIncomingCall(mPhoneAccountA0.getAccountHandle(), extras);
 
-        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+                TEST_TIMEOUT);
         verify(mConnectionServiceFixtureA.getTestDouble())
                 .createConnection(any(PhoneAccountHandle.class), anyString(),
                         any(ConnectionRequest.class), eq(true), eq(false), any());
 
-        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+                TEST_TIMEOUT);
         // Never reply to the caller info lookup.
         assertEquals(1, mCallerInfoAsyncQueryFactoryFixture.mRequests.size());
 
@@ -460,12 +468,14 @@
         mTelecomSystem.getTelecomServiceImpl().getBinder()
                 .addNewIncomingCall(mPhoneAccountA0.getAccountHandle(), extras);
 
-        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+                TEST_TIMEOUT);
         verify(mConnectionServiceFixtureA.getTestDouble())
                 .createConnection(any(PhoneAccountHandle.class), anyString(),
                         any(ConnectionRequest.class), eq(true), eq(false), any());
 
-        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+                TEST_TIMEOUT);
         assertEquals(1, mCallerInfoAsyncQueryFactoryFixture.mRequests.size());
         for (CallerInfoAsyncQueryFactoryFixture.Request request :
                 mCallerInfoAsyncQueryFactoryFixture.mRequests) {
@@ -554,10 +564,12 @@
         // TODO: We have to use the same PhoneAccount for both; see http://b/18461539
         IdPair outgoing = startAndMakeActiveOutgoingCall("650-555-1212",
                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
-        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+                TEST_TIMEOUT);
         IdPair incoming = startAndMakeActiveIncomingCall("650-555-2323",
                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
-        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+                TEST_TIMEOUT);
         verify(mConnectionServiceFixtureA.getTestDouble())
                 .hold(eq(outgoing.mConnectionId), any());
         mConnectionServiceFixtureA.mConnectionById.get(outgoing.mConnectionId).state =
@@ -786,7 +798,8 @@
             @Override
             public Bundle answer(InvocationOnMock invocation) throws Throwable {
                 Bundle bundle = new Bundle();
-                bundle.putBoolean(BlockedNumberContract.RES_NUMBER_IS_BLOCKED, true);
+                bundle.putInt(BlockedNumberContract.RES_BLOCK_STATUS,
+                        BlockedNumberContract.STATUS_BLOCKED_IN_LIST);
                 return bundle;
             }
         });
@@ -795,6 +808,7 @@
     private void blockNumberWithAnswer(String phoneNumber, Answer answer) throws Exception {
         when(getBlockedNumberProvider().call(
                 anyString(),
+                anyString(),
                 eq(BlockedNumberContract.SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER),
                 eq(phoneNumber),
                 nullable(Bundle.class))).thenAnswer(answer);
@@ -1051,7 +1065,8 @@
 
         // Route self-managed call to speaker.
         connection.setAudioRoute(CallAudioState.ROUTE_SPEAKER);
-        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        waitForHandlerAction(mConnectionServiceFixtureA.mConnectionServiceDelegate.getHandler(),
+                TEST_TIMEOUT);
 
         // Place an emergency call.
         startAndMakeDialingEmergencyCall("650-555-1212", mPhoneAccountE0.getAccountHandle(),
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
index 2129ffa..63ff962 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
@@ -31,6 +31,7 @@
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
 import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -96,6 +97,12 @@
         mBluetoothDeviceManager.setHearingAidServiceForTesting(mBluetoothHearingAid);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testSingleDeviceConnectAndDisconnect() {
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
index 93c2909..b20ecfb 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
@@ -31,6 +31,7 @@
 import com.android.server.telecom.bluetooth.BluetoothDeviceManager;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -71,6 +72,12 @@
         super.setUp();
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testConnectHfpRetryWhileNotConnected() {
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
index 2584f33..8ba6b63 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
@@ -30,6 +30,7 @@
 import com.android.server.telecom.bluetooth.BluetoothDeviceManager;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -246,6 +247,12 @@
         super.setUp();
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     public BluetoothRouteTransitionTests(BluetoothRouteTestParameters params) {
         mParams = params;
     }
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java b/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java
index 01add22..3251d7d 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java
@@ -19,20 +19,24 @@
 import android.media.ToneGenerator;
 import android.telecom.DisconnectCause;
 import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.util.SparseArray;
 
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallAudioModeStateMachine;
+import com.android.server.telecom.CallAudioModeStateMachine.MessageArgs;
 import com.android.server.telecom.CallAudioRouteStateMachine;
 import com.android.server.telecom.CallState;
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.CallAudioManager;
 import com.android.server.telecom.DtmfLocalTonePlayer;
 import com.android.server.telecom.InCallTonePlayer;
+import com.android.server.telecom.CallAudioModeStateMachine.MessageArgs.Builder;
 import com.android.server.telecom.RingbackPlayer;
 import com.android.server.telecom.Ringer;
 import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -45,13 +49,16 @@
 import java.util.stream.Collectors;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -92,17 +99,22 @@
                 mDtmfLocalTonePlayer);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @MediumTest
     @Test
     public void testUnmuteOfSecondIncomingCall() {
         // Start with a single incoming call.
         Call call = createIncomingCall();
+        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor();
         when(call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO))
                 .thenReturn(false);
         when(call.getId()).thenReturn("1");
 
-        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
-                ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
         // Answer the incoming call
         mCallAudioManager.onIncomingCallAnswered(call);
         when(call.getState()).thenReturn(CallState.ACTIVE);
@@ -110,14 +122,15 @@
         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
                 eq(CallAudioModeStateMachine.NO_MORE_RINGING_CALLS), captor.capture());
         CallAudioModeStateMachine.MessageArgs correctArgs =
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                );
+                new Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setHasAudioProcessingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build();
         assertMessageArgEquality(correctArgs, captor.getValue());
         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
@@ -160,11 +173,10 @@
     @Test
     public void testSingleIncomingCallFlowWithoutMTSpeedUp() {
         Call call = createIncomingCall();
+        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor();
         when(call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO))
                 .thenReturn(false);
 
-        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
-                ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
         // Answer the incoming call
         mCallAudioManager.onIncomingCallAnswered(call);
         when(call.getState()).thenReturn(CallState.ACTIVE);
@@ -172,14 +184,15 @@
         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
                 eq(CallAudioModeStateMachine.NO_MORE_RINGING_CALLS), captor.capture());
         CallAudioModeStateMachine.MessageArgs correctArgs =
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                );
+                new Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setHasAudioProcessingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build();
         assertMessageArgEquality(correctArgs, captor.getValue());
         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
@@ -196,12 +209,11 @@
     @Test
     public void testSingleIncomingCallFlowWithMTSpeedUp() {
         Call call = createIncomingCall();
+        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor();
         when(call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO))
                 .thenReturn(true);
         when(call.getState()).thenReturn(CallState.ANSWERED);
 
-        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
-                ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
         // Answer the incoming call
         mCallAudioManager.onCallStateChanged(call, CallState.RINGING, CallState.ANSWERED);
         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
@@ -209,14 +221,15 @@
         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
                 eq(CallAudioModeStateMachine.NO_MORE_RINGING_CALLS), captor.capture());
         CallAudioModeStateMachine.MessageArgs correctArgs =
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                );
+                new Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setHasAudioProcessingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build();
         assertMessageArgEquality(correctArgs, captor.getValue());
         assertMessageArgEquality(correctArgs, captor.getValue());
         when(call.getState()).thenReturn(CallState.ACTIVE);
@@ -233,25 +246,25 @@
     @Test
     public void testSingleOutgoingCall() {
         Call call = mock(Call.class);
+        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor();
         when(call.getState()).thenReturn(CallState.CONNECTING);
 
         mCallAudioManager.onCallAdded(call);
         assertEquals(call, mCallAudioManager.getForegroundCall());
-        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
-                ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
         verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo(
                 CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
                 eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
         CallAudioModeStateMachine.MessageArgs expectedArgs =
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                );
+                new Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setHasAudioProcessingCalls(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build();
         assertMessageArgEquality(expectedArgs, captor.getValue());
 
         when(call.getState()).thenReturn(CallState.DIALING);
@@ -278,6 +291,347 @@
         verifyProperCleanup();
     }
 
+    @SmallTest
+    @Test
+    public void testNewCallGoesToAudioProcessing() {
+        Call call = mock(Call.class);
+        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor();
+        when(call.getState()).thenReturn(CallState.NEW);
+
+        // Make sure nothing happens when we add the NEW call
+        mCallAudioManager.onCallAdded(call);
+
+        verify(mCallAudioRouteStateMachine, never()).sendMessageWithSessionInfo(anyInt());
+        verify(mCallAudioModeStateMachine, never()).sendMessageWithArgs(
+                anyInt(), nullable(MessageArgs.class));
+
+        // Transition the call to AUDIO_PROCESSING and see what happens
+        when(call.getState()).thenReturn(CallState.AUDIO_PROCESSING);
+        mCallAudioManager.onCallStateChanged(call, CallState.NEW, CallState.AUDIO_PROCESSING);
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NEW_AUDIO_PROCESSING_CALL), captor.capture());
+
+        CallAudioModeStateMachine.MessageArgs expectedArgs =
+                new Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setHasAudioProcessingCalls(true)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build();
+        assertMessageArgEquality(expectedArgs, captor.getValue());
+    }
+
+    @SmallTest
+    @Test
+    public void testRingingCallGoesToAudioProcessing() {
+        Call call = mock(Call.class);
+        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor();
+        when(call.getState()).thenReturn(CallState.RINGING);
+
+        // Make sure appropriate messages are sent when we add a RINGING call
+        mCallAudioManager.onCallAdded(call);
+
+        assertEquals(call, mCallAudioManager.getForegroundCall());
+        verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NEW_RINGING_CALL), captor.capture());
+        CallAudioModeStateMachine.MessageArgs expectedArgs =
+                new Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(true)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setHasAudioProcessingCalls(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build();
+        assertMessageArgEquality(expectedArgs, captor.getValue());
+
+        // Transition the call to AUDIO_PROCESSING and see what happens
+        when(call.getState()).thenReturn(CallState.AUDIO_PROCESSING);
+        mCallAudioManager.onCallStateChanged(call, CallState.RINGING, CallState.AUDIO_PROCESSING);
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NEW_AUDIO_PROCESSING_CALL), captor.capture());
+
+        CallAudioModeStateMachine.MessageArgs expectedArgs2 =
+                new Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setHasAudioProcessingCalls(true)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build();
+        assertMessageArgEquality(expectedArgs2, captor.getValue());
+
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NO_MORE_RINGING_CALLS), captor.capture());
+        assertMessageArgEquality(expectedArgs2, captor.getValue());
+    }
+
+    @SmallTest
+    @Test
+    public void testActiveCallGoesToAudioProcessing() {
+        Call call = mock(Call.class);
+        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor();
+        when(call.getState()).thenReturn(CallState.ACTIVE);
+
+        // Make sure appropriate messages are sent when we add an active call
+        mCallAudioManager.onCallAdded(call);
+
+        assertEquals(call, mCallAudioManager.getForegroundCall());
+        verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
+        CallAudioModeStateMachine.MessageArgs expectedArgs =
+                new Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setHasAudioProcessingCalls(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build();
+        assertMessageArgEquality(expectedArgs, captor.getValue());
+
+        // Transition the call to AUDIO_PROCESSING and see what happens
+        when(call.getState()).thenReturn(CallState.AUDIO_PROCESSING);
+        mCallAudioManager.onCallStateChanged(call, CallState.ACTIVE, CallState.AUDIO_PROCESSING);
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NEW_AUDIO_PROCESSING_CALL), captor.capture());
+
+        CallAudioModeStateMachine.MessageArgs expectedArgs2 =
+                new Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setHasAudioProcessingCalls(true)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build();
+        assertMessageArgEquality(expectedArgs2, captor.getValue());
+
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS), captor.capture());
+        assertMessageArgEquality(expectedArgs2, captor.getValue());
+    }
+
+    @SmallTest
+    @Test
+    public void testAudioProcessingCallDisconnects() {
+        Call call = createAudioProcessingCall();
+        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor();
+
+        when(call.getState()).thenReturn(CallState.DISCONNECTED);
+        when(call.getDisconnectCause()).thenReturn(new DisconnectCause(DisconnectCause.LOCAL,
+                "", "", "", ToneGenerator.TONE_UNKNOWN));
+
+        mCallAudioManager.onCallStateChanged(call, CallState.AUDIO_PROCESSING,
+                CallState.DISCONNECTED);
+        verify(mPlayerFactory, never()).createPlayer(anyInt());
+        CallAudioModeStateMachine.MessageArgs expectedArgs2 = new Builder()
+                .setHasActiveOrDialingCalls(false)
+                .setHasRingingCalls(false)
+                .setHasHoldingCalls(false)
+                .setHasAudioProcessingCalls(false)
+                .setIsTonePlaying(false)
+                .setForegroundCallIsVoip(false)
+                .setSession(null)
+                .build();
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NO_MORE_AUDIO_PROCESSING_CALLS), captor.capture());
+        assertMessageArgEquality(expectedArgs2, captor.getValue());
+        verify(mCallAudioModeStateMachine, never()).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.TONE_STARTED_PLAYING), nullable(MessageArgs.class));
+
+        mCallAudioManager.onCallRemoved(call);
+        verifyProperCleanup();
+    }
+
+    @SmallTest
+    @Test
+    public void testAudioProcessingCallDoesSimulatedRing() {
+        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor();
+
+        Call call = createAudioProcessingCall();
+
+        when(call.getState()).thenReturn(CallState.SIMULATED_RINGING);
+
+        mCallAudioManager.onCallStateChanged(call, CallState.AUDIO_PROCESSING,
+                CallState.SIMULATED_RINGING);
+        verify(mPlayerFactory, never()).createPlayer(anyInt());
+        CallAudioModeStateMachine.MessageArgs expectedArgs = new Builder()
+                .setHasActiveOrDialingCalls(false)
+                .setHasRingingCalls(true)
+                .setHasHoldingCalls(false)
+                .setHasAudioProcessingCalls(false)
+                .setIsTonePlaying(false)
+                .setForegroundCallIsVoip(false)
+                .setSession(null)
+                .build();
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NO_MORE_AUDIO_PROCESSING_CALLS), captor.capture());
+        assertMessageArgEquality(expectedArgs, captor.getValue());
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NEW_RINGING_CALL), captor.capture());
+        assertMessageArgEquality(expectedArgs, captor.getValue());
+    }
+
+    @SmallTest
+    @Test
+    public void testAudioProcessingCallGoesActive() {
+        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor();
+
+        Call call = createAudioProcessingCall();
+
+        when(call.getState()).thenReturn(CallState.ACTIVE);
+
+        mCallAudioManager.onCallStateChanged(call, CallState.AUDIO_PROCESSING,
+                CallState.ACTIVE);
+        verify(mPlayerFactory, never()).createPlayer(anyInt());
+        CallAudioModeStateMachine.MessageArgs expectedArgs = new Builder()
+                .setHasActiveOrDialingCalls(true)
+                .setHasRingingCalls(false)
+                .setHasHoldingCalls(false)
+                .setHasAudioProcessingCalls(false)
+                .setIsTonePlaying(false)
+                .setForegroundCallIsVoip(false)
+                .setSession(null)
+                .build();
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NO_MORE_AUDIO_PROCESSING_CALLS), captor.capture());
+        assertMessageArgEquality(expectedArgs, captor.getValue());
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
+        assertMessageArgEquality(expectedArgs, captor.getValue());
+    }
+
+    @SmallTest
+    @Test
+    public void testSimulatedRingingCallGoesActive() {
+        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor();
+
+        Call call = createSimulatedRingingCall();
+
+        when(call.getState()).thenReturn(CallState.ACTIVE);
+
+        mCallAudioManager.onCallStateChanged(call, CallState.SIMULATED_RINGING,
+                CallState.ACTIVE);
+        verify(mPlayerFactory, never()).createPlayer(anyInt());
+        CallAudioModeStateMachine.MessageArgs expectedArgs = new Builder()
+                .setHasActiveOrDialingCalls(true)
+                .setHasRingingCalls(false)
+                .setHasHoldingCalls(false)
+                .setHasAudioProcessingCalls(false)
+                .setIsTonePlaying(false)
+                .setForegroundCallIsVoip(false)
+                .setSession(null)
+                .build();
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NO_MORE_RINGING_CALLS), captor.capture());
+        assertMessageArgEquality(expectedArgs, captor.getValue());
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL), captor.capture());
+        assertMessageArgEquality(expectedArgs, captor.getValue());
+    }
+
+    private Call createAudioProcessingCall() {
+        Call call = mock(Call.class);
+        when(call.getState()).thenReturn(CallState.AUDIO_PROCESSING);
+        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor();
+
+        // Set up an AUDIO_PROCESSING call
+        mCallAudioManager.onCallAdded(call);
+
+        assertNull(mCallAudioManager.getForegroundCall());
+
+        verify(mCallAudioRouteStateMachine, never()).sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NEW_AUDIO_PROCESSING_CALL), captor.capture());
+        CallAudioModeStateMachine.MessageArgs expectedArgs =
+                new Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setHasAudioProcessingCalls(true)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build();
+        assertMessageArgEquality(expectedArgs, captor.getValue());
+
+        return call;
+    }
+
+    @SmallTest
+    @Test
+    public void testSimulatedRingingCallDisconnects() {
+        Call call = createSimulatedRingingCall();
+        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor();
+
+        when(call.getState()).thenReturn(CallState.DISCONNECTED);
+        when(call.getDisconnectCause()).thenReturn(new DisconnectCause(DisconnectCause.LOCAL,
+                "", "", "", ToneGenerator.TONE_UNKNOWN));
+
+        mCallAudioManager.onCallStateChanged(call, CallState.SIMULATED_RINGING,
+                CallState.DISCONNECTED);
+        verify(mPlayerFactory, never()).createPlayer(anyInt());
+        CallAudioModeStateMachine.MessageArgs expectedArgs2 = new Builder()
+                .setHasActiveOrDialingCalls(false)
+                .setHasRingingCalls(false)
+                .setHasHoldingCalls(false)
+                .setHasAudioProcessingCalls(false)
+                .setIsTonePlaying(false)
+                .setForegroundCallIsVoip(false)
+                .setSession(null)
+                .build();
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NO_MORE_RINGING_CALLS), captor.capture());
+        assertMessageArgEquality(expectedArgs2, captor.getValue());
+        verify(mCallAudioModeStateMachine, never()).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.TONE_STARTED_PLAYING), nullable(MessageArgs.class));
+
+        mCallAudioManager.onCallRemoved(call);
+        verifyProperCleanup();
+    }
+
+    private Call createSimulatedRingingCall() {
+        Call call = mock(Call.class);
+        when(call.getState()).thenReturn(CallState.SIMULATED_RINGING);
+        ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor = makeNewCaptor();
+
+        mCallAudioManager.onCallAdded(call);
+
+        assertEquals(call, mCallAudioManager.getForegroundCall());
+
+        verify(mCallAudioRouteStateMachine).sendMessageWithSessionInfo(
+                CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
+        verify(mCallAudioModeStateMachine).sendMessageWithArgs(
+                eq(CallAudioModeStateMachine.NEW_RINGING_CALL), captor.capture());
+        CallAudioModeStateMachine.MessageArgs expectedArgs =
+                new Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(true)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setHasAudioProcessingCalls(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build();
+        assertMessageArgEquality(expectedArgs, captor.getValue());
+
+        return call;
+    }
+
     private Call createIncomingCall() {
         Call call = mock(Call.class);
         when(call.getState()).thenReturn(CallState.RINGING);
@@ -290,14 +644,15 @@
                 CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE);
         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
                 eq(CallAudioModeStateMachine.NEW_RINGING_CALL), captor.capture());
-        assertMessageArgEquality(new CallAudioModeStateMachine.MessageArgs(
-                false, // hasActiveOrDialingCalls
-                true, // hasRingingCalls
-                false, // hasHoldingCalls
-                false, // isTonePlaying
-                false, // foregroundCallIsVoip
-                null // session
-        ), captor.getValue());
+        assertMessageArgEquality(new Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(true)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
+                captor.getValue());
 
         return call;
     }
@@ -313,14 +668,14 @@
 
         mCallAudioManager.onCallStateChanged(call, CallState.ACTIVE, CallState.DISCONNECTED);
         verify(mPlayerFactory).createPlayer(InCallTonePlayer.TONE_CALL_ENDED);
-        correctArgs = new CallAudioModeStateMachine.MessageArgs(
-                false, // hasActiveOrDialingCalls
-                false, // hasRingingCalls
-                false, // hasHoldingCalls
-                true, // isTonePlaying
-                false, // foregroundCallIsVoip
-                null // session
-        );
+        correctArgs = new Builder()
+                .setHasActiveOrDialingCalls(false)
+                .setHasRingingCalls(false)
+                .setHasHoldingCalls(false)
+                .setIsTonePlaying(true)
+                .setForegroundCallIsVoip(false)
+                .setSession(null)
+                .build();
         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
                 eq(CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS), captor.capture());
         assertMessageArgEquality(correctArgs, captor.getValue());
@@ -333,15 +688,14 @@
         ArgumentCaptor<CallAudioModeStateMachine.MessageArgs> captor =
                 ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
         mCallAudioManager.setIsTonePlaying(false);
-        CallAudioModeStateMachine.MessageArgs correctArgs =
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                );
+        CallAudioModeStateMachine.MessageArgs correctArgs = new Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build();
         verify(mCallAudioModeStateMachine).sendMessageWithArgs(
                 eq(CallAudioModeStateMachine.TONE_STOPPED_PLAYING), captor.capture());
         assertMessageArgEquality(correctArgs, captor.getValue());
@@ -355,6 +709,10 @@
         }
     }
 
+    private ArgumentCaptor<MessageArgs> makeNewCaptor() {
+        return ArgumentCaptor.forClass(CallAudioModeStateMachine.MessageArgs.class);
+    }
+
     private void assertMessageArgEquality(CallAudioModeStateMachine.MessageArgs expected,
             CallAudioModeStateMachine.MessageArgs actual) {
         assertEquals(expected.hasActiveOrDialingCalls, actual.hasActiveOrDialingCalls);
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java
index 56f585f..ca84c4c 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java
@@ -17,13 +17,16 @@
 package com.android.server.telecom.tests;
 
 import android.media.AudioManager;
+import android.os.HandlerThread;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.server.telecom.CallAudioManager;
 import com.android.server.telecom.CallAudioModeStateMachine;
 import com.android.server.telecom.CallAudioRouteStateMachine;
+import com.android.server.telecom.CallAudioModeStateMachine.MessageArgs.Builder;
 import com.android.server.telecom.SystemStateHelper;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -47,17 +50,29 @@
     @Mock private AudioManager mAudioManager;
     @Mock private CallAudioManager mCallAudioManager;
 
+    private HandlerThread mTestThread;
+
     @Override
     @Before
     public void setUp() throws Exception {
+        mTestThread = new HandlerThread("CallAudioModeStateMachineTest");
+        mTestThread.start();
         super.setUp();
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        mTestThread.quit();
+        mTestThread.join();
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testNoFocusWhenRingerSilenced() throws Throwable {
         CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
-                mAudioManager);
+                mAudioManager, mTestThread.getLooper());
         sm.setCallAudioManager(mCallAudioManager);
         sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
@@ -65,15 +80,14 @@
         resetMocks();
         when(mCallAudioManager.startRinging()).thenReturn(false);
 
-        sm.sendMessage(CallAudioModeStateMachine.NEW_RINGING_CALL,
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        true, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ));
+        sm.sendMessage(CallAudioModeStateMachine.NEW_RINGING_CALL, new Builder()
+                .setHasActiveOrDialingCalls(false)
+                .setHasRingingCalls(true)
+                .setHasHoldingCalls(false)
+                .setIsTonePlaying(false)
+                .setForegroundCallIsVoip(false)
+                .setSession(null)
+                .build());
         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
 
         assertEquals(CallAudioModeStateMachine.RING_STATE_NAME, sm.getCurrentStateName());
@@ -90,32 +104,30 @@
     @Test
     public void testNoRingWhenDeviceIsAtEar() {
         CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
-                mAudioManager);
+                mAudioManager, mTestThread.getLooper());
         sm.setCallAudioManager(mCallAudioManager);
         sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
-        sm.sendMessage(CallAudioModeStateMachine.NEW_HOLDING_CALL,
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        true, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ));
+        sm.sendMessage(CallAudioModeStateMachine.NEW_HOLDING_CALL, new Builder()
+                .setHasActiveOrDialingCalls(false)
+                .setHasRingingCalls(false)
+                .setHasHoldingCalls(true)
+                .setIsTonePlaying(false)
+                .setForegroundCallIsVoip(false)
+                .setSession(null)
+                .build());
         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
         assertEquals(CallAudioModeStateMachine.TONE_HOLD_STATE_NAME, sm.getCurrentStateName());
         when(mSystemStateHelper.isDeviceAtEar()).thenReturn(true);
 
         resetMocks();
-        sm.sendMessage(CallAudioModeStateMachine.NEW_RINGING_CALL,
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        true, // hasRingingCalls
-                        true, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ));
+        sm.sendMessage(CallAudioModeStateMachine.NEW_RINGING_CALL, new Builder()
+                .setHasActiveOrDialingCalls(false)
+                .setHasRingingCalls(true)
+                .setHasHoldingCalls(true)
+                .setIsTonePlaying(false)
+                .setForegroundCallIsVoip(false)
+                .setSession(null)
+                .build());
         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
 
         verify(mAudioManager, never()).requestAudioFocusForCall(anyInt(), anyInt());
@@ -128,7 +140,7 @@
     @Test
     public void testRegainFocusWhenHfpIsConnectedSilenced() throws Throwable {
         CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
-                mAudioManager);
+                mAudioManager, mTestThread.getLooper());
         sm.setCallAudioManager(mCallAudioManager);
         sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
@@ -136,15 +148,14 @@
         resetMocks();
         when(mCallAudioManager.startRinging()).thenReturn(false);
 
-        sm.sendMessage(CallAudioModeStateMachine.NEW_RINGING_CALL,
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        true, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ));
+        sm.sendMessage(CallAudioModeStateMachine.NEW_RINGING_CALL, new Builder()
+                .setHasActiveOrDialingCalls(false)
+                .setHasRingingCalls(true)
+                .setHasHoldingCalls(false)
+                .setIsTonePlaying(false)
+                .setForegroundCallIsVoip(false)
+                .setSession(null)
+                .build());
         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
 
         assertEquals(CallAudioModeStateMachine.RING_STATE_NAME, sm.getCurrentStateName());
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioModeTransitionTests.java b/tests/src/com/android/server/telecom/tests/CallAudioModeTransitionTests.java
index 81339ed..2047867 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioModeTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioModeTransitionTests.java
@@ -16,14 +16,16 @@
 
 package com.android.server.telecom.tests;
 
-import android.content.Context;
 import android.media.AudioManager;
+import android.os.HandlerThread;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.server.telecom.CallAudioManager;
 import com.android.server.telecom.CallAudioModeStateMachine;
+import com.android.server.telecom.CallAudioModeStateMachine.MessageArgs;
 import com.android.server.telecom.SystemStateHelper;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -102,13 +104,24 @@
     @Mock private AudioManager mAudioManager;
     @Mock private CallAudioManager mCallAudioManager;
     private final ModeTestParameters mParams;
+    private HandlerThread mTestThread;
 
     @Override
     @Before
     public void setUp() throws Exception {
+        mTestThread = new HandlerThread("CallAudioModeStateMachineTest");
+        mTestThread.start();
         super.setUp();
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        mTestThread.quit();
+        mTestThread.join();
+        super.tearDown();
+    }
+
     public CallAudioModeTransitionTests(ModeTestParameters params) {
         mParams = params;
     }
@@ -117,7 +130,7 @@
     @SmallTest
     public void modeTransitionTest() {
         CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mSystemStateHelper,
-                mAudioManager);
+                mAudioManager, mTestThread.getLooper());
         sm.setCallAudioManager(mCallAudioManager);
         sm.sendMessage(mParams.initialAudioState);
         waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
@@ -178,8 +191,6 @@
                 verify(mCallAudioManager).stopCallWaiting();
                 break;
         }
-
-        sm.quitNow();
     }
 
     @Parameterized.Parameters(name = "{0}")
@@ -189,14 +200,14 @@
                 "New active/dialing call with no other calls when unfocused",
                 CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
                 FOCUS_VOICE, // expectedFocus
                 AudioManager.MODE_IN_CALL, // expectedMode
@@ -208,14 +219,14 @@
                 "New active/dialing voip call with no other calls when unfocused",
                 CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        true, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(true)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.COMMS_STATE_NAME, // expectedFinalStateName
                 FOCUS_VOICE, // expectedFocus
                 AudioManager.MODE_IN_COMMUNICATION, // expectedMode
@@ -227,14 +238,14 @@
                 "New ringing call with no other calls when unfocused",
                 CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NEW_RINGING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        true, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(true)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.RING_STATE_NAME, // expectedFinalStateName
                 FOCUS_RING, // expectedFocus
                 AudioManager.MODE_RINGTONE, // expectedMode
@@ -246,14 +257,14 @@
                 "New ringing call coming in on top of active/dialing call",
                 CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NEW_RINGING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        true, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(true)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
                 NO_CHANGE, // expectedFocus
                 NO_CHANGE, // expectedMode
@@ -265,14 +276,14 @@
                 "Ringing call becomes active, part 1",
                 CallAudioModeStateMachine.ENTER_RING_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
                 FOCUS_VOICE, // expectedFocus
                 AudioManager.MODE_IN_CALL, // expectedMode
@@ -284,14 +295,14 @@
                 "Ringing call becomes active, part 2",
                 CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
                 NO_CHANGE, // expectedFocus
                 NO_CHANGE, // expectedMode
@@ -303,14 +314,14 @@
                 "Active call disconnects, but tone is playing",
                 CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        true, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(true)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.TONE_HOLD_STATE_NAME, // expectedFinalStateName
                 FOCUS_VOICE, // expectedFocus
                 AudioManager.MODE_IN_CALL, // expectedMode
@@ -322,14 +333,14 @@
                 "Tone stops playing, with no active calls",
                 CallAudioModeStateMachine.ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.TONE_STOPPED_PLAYING, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.UNFOCUSED_STATE_NAME, // expectedFinalStateName
                 FOCUS_OFF, // expectedFocus
                 AudioManager.MODE_NORMAL, // expectedMode
@@ -341,14 +352,14 @@
                 "Ringing call disconnects",
                 CallAudioModeStateMachine.ENTER_RING_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.UNFOCUSED_STATE_NAME, // expectedFinalStateName
                 FOCUS_OFF, // expectedFocus
                 AudioManager.MODE_NORMAL, // expectedMode
@@ -360,14 +371,14 @@
                 "Call-waiting call disconnects",
                 CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        true, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(true)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
                 FOCUS_NO_CHANGE, // expectedFocus
                 NO_CHANGE, // expectedMode
@@ -379,14 +390,14 @@
                 "Call is placed on hold - 1",
                 CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        true, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(true)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.TONE_HOLD_STATE_NAME, // expectedFinalStateName
                 FOCUS_VOICE, // expectedFocus
                 AudioManager.MODE_IN_CALL, // expectedMode
@@ -398,14 +409,14 @@
                 "Call is placed on hold - 2",
                 CallAudioModeStateMachine.ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NEW_HOLDING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        true, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(true)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.TONE_HOLD_STATE_NAME, // expectedFinalStateName
                 FOCUS_NO_CHANGE, // expectedFocus
                 NO_CHANGE, // expectedMode
@@ -414,17 +425,93 @@
         ));
 
         result.add(new ModeTestParameters(
+                "Swap between voip and sim calls - 1",
+                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NEW_HOLDING_CALL, // messageType
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(true)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(true)
+                        .setSession(null)
+                        .build(),
+                CallAudioModeStateMachine.COMMS_STATE_NAME, // expectedFinalStateName
+                FOCUS_VOICE, // expectedFocus
+                AudioManager.MODE_IN_COMMUNICATION, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Swap between voip and sim calls - 2",
+                CallAudioModeStateMachine.ENTER_COMMS_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NEW_HOLDING_CALL, // messageType
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(true)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
+                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
+                FOCUS_VOICE, // expectedFocus
+                AudioManager.MODE_IN_CALL, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Swap between voip and sim calls - 3",
+                CallAudioModeStateMachine.ENTER_COMMS_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(true)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
+                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
+                FOCUS_VOICE, // expectedFocus
+                AudioManager.MODE_IN_CALL, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Swap between voip and sim calls - 4",
+                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NEW_HOLDING_CALL, // messageType
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(true)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(true)
+                        .setSession(null)
+                        .build(),
+                CallAudioModeStateMachine.COMMS_STATE_NAME, // expectedFinalStateName
+                FOCUS_VOICE, // expectedFocus
+                AudioManager.MODE_IN_COMMUNICATION, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
                 "Call is taken off hold - 1",
                 CallAudioModeStateMachine.ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NO_MORE_HOLDING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
                 FOCUS_VOICE, // expectedFocus
                 AudioManager.MODE_IN_CALL, // expectedMode
@@ -436,14 +523,14 @@
                 "Call is taken off hold - 2",
                 CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
                 FOCUS_NO_CHANGE, // expectedFocus
                 NO_CHANGE, // expectedMode
@@ -455,14 +542,14 @@
                 "Active call disconnects while there's a call-waiting call",
                 CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        true, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        true, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(true)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(true)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.RING_STATE_NAME, // expectedFinalStateName
                 FOCUS_RING, // expectedFocus
                 AudioManager.MODE_RINGTONE, // expectedMode
@@ -474,14 +561,14 @@
                 "New dialing call when there's a call on hold",
                 CallAudioModeStateMachine.ENTER_TONE_OR_HOLD_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        true, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(true)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
                 FOCUS_VOICE, // expectedFocus
                 AudioManager.MODE_IN_CALL, // expectedMode
@@ -493,14 +580,14 @@
                 "Ringing call disconnects with a holding call in the background",
                 CallAudioModeStateMachine.ENTER_RING_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        false, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        true, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(true)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.TONE_HOLD_STATE_NAME, // expectedFinalStateName
                 FOCUS_VOICE, // expectedFocus
                 AudioManager.MODE_NORMAL, // expectedMode -- we're expecting this because
@@ -513,14 +600,14 @@
                 "Foreground call transitions from sim to voip",
                 CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        true, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(true)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.COMMS_STATE_NAME, // expectedFinalStateName
                 FOCUS_VOICE, // expectedFocus
                 AudioManager.MODE_IN_COMMUNICATION, // expectedMode
@@ -532,14 +619,14 @@
                 "Foreground call transitions from voip to sim",
                 CallAudioModeStateMachine.ENTER_COMMS_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        false, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
                 FOCUS_VOICE, // expectedFocus
                 AudioManager.MODE_IN_CALL, // expectedMode
@@ -552,14 +639,14 @@
                         "foreground",
                 CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        true, // isTonePlaying
-                        false, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(true)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
                 FOCUS_NO_CHANGE, // expectedFocus
                 NO_CHANGE, // expectedMode
@@ -572,14 +659,14 @@
                         "foreground",
                 CallAudioModeStateMachine.ENTER_COMMS_FOCUS_FOR_TESTING, // initialAudioState
                 CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
-                new CallAudioModeStateMachine.MessageArgs(
-                        true, // hasActiveOrDialingCalls
-                        false, // hasRingingCalls
-                        false, // hasHoldingCalls
-                        true, // isTonePlaying
-                        true, // foregroundCallIsVoip
-                        null // session
-                ),
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setIsTonePlaying(true)
+                        .setForegroundCallIsVoip(true)
+                        .setSession(null)
+                        .build(),
                 CallAudioModeStateMachine.COMMS_STATE_NAME, // expectedFinalStateName
                 FOCUS_NO_CHANGE, // expectedFocus
                 NO_CHANGE, // expectedMode
@@ -587,6 +674,188 @@
                 OFF // expectedCallWaitingInteraction
         ));
 
+        result.add(new ModeTestParameters(
+                "Call enters audio processing state from call screening service",
+                CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NEW_AUDIO_PROCESSING_CALL, // messageType
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setHasAudioProcessingCalls(true)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
+                CallAudioModeStateMachine.AUDIO_PROCESSING_STATE_NAME, // expectedFinalStateName
+                FOCUS_NO_CHANGE, // expectedFocus
+                CallAudioModeStateMachine.NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Call enters audio processing state by manual intervention from ringing state, 1",
+                CallAudioModeStateMachine.ENTER_RING_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, // messageType
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setHasAudioProcessingCalls(true)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
+                CallAudioModeStateMachine.AUDIO_PROCESSING_STATE_NAME, // expectedFinalStateName
+                FOCUS_OFF, // expectedFocus
+                CallAudioModeStateMachine.NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING, // expectedMode
+                OFF, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Call enters audio processing state by manual intervention from ringing state, 2",
+                CallAudioModeStateMachine.ENTER_RING_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NEW_AUDIO_PROCESSING_CALL, // messageType
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setHasAudioProcessingCalls(true)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
+                CallAudioModeStateMachine.AUDIO_PROCESSING_STATE_NAME, // expectedFinalStateName
+                FOCUS_OFF, // expectedFocus
+                CallAudioModeStateMachine.NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING, // expectedMode
+                OFF, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Call enters audio processing state from active call, 1",
+                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS, // messageType
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setHasAudioProcessingCalls(true)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
+                CallAudioModeStateMachine.AUDIO_PROCESSING_STATE_NAME, // expectedFinalStateName
+                FOCUS_OFF, // expectedFocus
+                CallAudioModeStateMachine.NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Call enters audio processing state from active call, 2",
+                CallAudioModeStateMachine.ENTER_CALL_FOCUS_FOR_TESTING, // initialAudioState
+                CallAudioModeStateMachine.NEW_AUDIO_PROCESSING_CALL, // messageType
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setHasAudioProcessingCalls(true)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
+                CallAudioModeStateMachine.AUDIO_PROCESSING_STATE_NAME, // expectedFinalStateName
+                FOCUS_OFF, // expectedFocus
+                CallAudioModeStateMachine.NEW_AUDIO_MODE_FOR_AUDIO_PROCESSING, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Call in audio processing gets hanged up",
+                CallAudioModeStateMachine.ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING, // initialAudioS
+                CallAudioModeStateMachine.NO_MORE_AUDIO_PROCESSING_CALLS, // messageType
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setHasAudioProcessingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
+                CallAudioModeStateMachine.UNFOCUSED_STATE_NAME, // expectedFinalStateName
+                NO_CHANGE, // expectedFocus
+                AudioManager.MODE_NORMAL, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Notify user of a call in audio processing by simulating ringing, 1",
+                CallAudioModeStateMachine.ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING, // initialAudioS
+                CallAudioModeStateMachine.NO_MORE_AUDIO_PROCESSING_CALLS, // messageType
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(true)
+                        .setHasHoldingCalls(false)
+                        .setHasAudioProcessingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
+                CallAudioModeStateMachine.RING_STATE_NAME, // expectedFinalStateName
+                FOCUS_RING, // expectedFocus
+                AudioManager.MODE_RINGTONE, // expectedMode
+                ON, // expectedRingingInteraction
+                // We expect a call to stopCallWaiting because it happens whenever the ringer starts
+                OFF // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Notify user of a call in audio processing by simulating ringing, 2",
+                CallAudioModeStateMachine.ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING, // initialAudioS
+                CallAudioModeStateMachine.NEW_RINGING_CALL, // messageType
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(false)
+                        .setHasRingingCalls(true)
+                        .setHasHoldingCalls(false)
+                        .setHasAudioProcessingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
+                CallAudioModeStateMachine.RING_STATE_NAME, // expectedFinalStateName
+                FOCUS_RING, // expectedFocus
+                AudioManager.MODE_RINGTONE, // expectedMode
+                ON, // expectedRingingInteraction
+                // We expect a call to stopCallWaiting because it happens whenever the ringer starts
+                OFF // expectedCallWaitingInteraction
+        ));
+
+        result.add(new ModeTestParameters(
+                "Audio processing call gets set to active manually",
+                CallAudioModeStateMachine.ENTER_AUDIO_PROCESSING_FOCUS_FOR_TESTING, // initialAudioS
+                CallAudioModeStateMachine.NO_MORE_AUDIO_PROCESSING_CALLS, // messageType
+                new MessageArgs.Builder()
+                        .setHasActiveOrDialingCalls(true)
+                        .setHasRingingCalls(false)
+                        .setHasHoldingCalls(false)
+                        .setHasAudioProcessingCalls(false)
+                        .setIsTonePlaying(false)
+                        .setForegroundCallIsVoip(false)
+                        .setSession(null)
+                        .build(),
+                CallAudioModeStateMachine.CALL_STATE_NAME, // expectedFinalStateName
+                FOCUS_VOICE, // expectedFocus
+                AudioManager.MODE_IN_CALL, // expectedMode
+                NO_CHANGE, // expectedRingingInteraction
+                NO_CHANGE // expectedCallWaitingInteraction
+        ));
+
         return result;
     }
 
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
index 7f289b8..5a739a5 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.media.IAudioService;
+import android.os.HandlerThread;
 import android.telecom.CallAudioState;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -34,6 +35,7 @@
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.WiredHeadsetManager;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -66,7 +68,7 @@
 import static org.mockito.Mockito.when;
 
 @RunWith(JUnit4.class)
-public class CallAudioRouteStateMachineTest extends TelecomSystemTest {
+public class CallAudioRouteStateMachineTest extends TelecomTestCase {
 
     private static final BluetoothDevice bluetoothDevice1 =
             BluetoothRouteManagerTest.makeBluetoothDevice("00:00:00:00:00:01");
@@ -88,12 +90,15 @@
     private static final int TEST_TIMEOUT = 500;
     private AudioManager mockAudioManager;
     private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
+    private HandlerThread mThreadHandler;
 
     @Override
     @Before
     public void setUp() throws Exception {
         super.setUp();
         MockitoAnnotations.initMocks(this);
+        mThreadHandler = new HandlerThread("CallAudioRouteStateMachineTest");
+        mThreadHandler.start();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
         mockAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
 
@@ -115,6 +120,14 @@
                 any(CallAudioState.class));
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        mThreadHandler.quit();
+        mThreadHandler.join();
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testEarpieceAutodetect() {
@@ -125,7 +138,8 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT);
+                CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT,
+                mThreadHandler.getLooper());
 
         // Since we don't know if we're on a platform with an earpiece or not, all we can do
         // is ensure the stateMachine construction didn't fail.  But at least we exercised the
@@ -143,7 +157,8 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
+                mThreadHandler.getLooper());
 
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
@@ -187,7 +202,8 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
+                mThreadHandler.getLooper());
         stateMachine.setCallAudioManager(mockCallAudioManager);
 
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
@@ -230,7 +246,8 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
+                mThreadHandler.getLooper());
         stateMachine.setCallAudioManager(mockCallAudioManager);
         Collection<BluetoothDevice> availableDevices = Collections.singleton(bluetoothDevice1);
 
@@ -303,7 +320,8 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
+                mThreadHandler.getLooper());
         stateMachine.setCallAudioManager(mockCallAudioManager);
 
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
@@ -338,7 +356,8 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
+                mThreadHandler.getLooper());
         stateMachine.setCallAudioManager(mockCallAudioManager);
         setInBandRing(false);
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
@@ -392,7 +411,8 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
+                mThreadHandler.getLooper());
         stateMachine.setCallAudioManager(mockCallAudioManager);
         List<BluetoothDevice> availableDevices =
                 Arrays.asList(bluetoothDevice1, bluetoothDevice2, bluetoothDevice3);
@@ -441,7 +461,8 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
+                mThreadHandler.getLooper());
         stateMachine.setCallAudioManager(mockCallAudioManager);
         List<BluetoothDevice> availableDevices =
                 Arrays.asList(bluetoothDevice1, bluetoothDevice2);
@@ -554,7 +575,8 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
+                mThreadHandler.getLooper());
         stateMachine.initialize();
         assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
     }
@@ -575,7 +597,8 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                earpieceControl);
+                earpieceControl,
+                mThreadHandler.getLooper());
         stateMachine.initialize();
         assertEquals(expectedState, stateMachine.getCurrentCallAudioState());
     }
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
index e63fe9b..57462c4 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
@@ -33,6 +33,7 @@
 import android.media.AudioManager;
 import android.media.IAudioService;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.telecom.CallAudioState;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -46,6 +47,7 @@
 import com.android.server.telecom.WiredHeadsetManager;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -154,6 +156,7 @@
     private static final int TEST_TIMEOUT = 500;
     private AudioManager mockAudioManager;
     private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
+    private HandlerThread mHandlerThread;
 
     public CallAudioRouteTransitionTests(RoutingTestParameters params) {
         mParams = params;
@@ -164,6 +167,8 @@
     public void setUp() throws Exception {
         super.setUp();
         MockitoAnnotations.initMocks(this);
+        mHandlerThread = new HandlerThread("CallAudioRouteTransitionTests");
+        mHandlerThread.start();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
         mockAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
 
@@ -185,6 +190,14 @@
                 any(CallAudioState.class));
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        mHandlerThread.quit();
+        mHandlerThread.join();
+        super.tearDown();
+    }
+
     private void setupMocksForParams(final CallAudioRouteStateMachine sm,
             RoutingTestParameters params) {
         // Set up bluetooth and speakerphone state
@@ -241,7 +254,8 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                mParams.earpieceControl);
+                mParams.earpieceControl,
+                mHandlerThread.getLooper());
         stateMachine.setCallAudioManager(mockCallAudioManager);
 
         setupMocksForParams(stateMachine, mParams);
@@ -329,7 +343,8 @@
                 mockWiredHeadsetManager,
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
-                mParams.earpieceControl);
+                mParams.earpieceControl,
+                mHandlerThread.getLooper());
         stateMachine.setCallAudioManager(mockCallAudioManager);
 
         // Set up bluetooth and speakerphone state
diff --git a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
index ffd56fa..d53c73c 100644
--- a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
@@ -69,6 +69,7 @@
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.TelephonyUtil;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -173,6 +174,12 @@
         when(userManager.getUserInfo(eq(MANAGED_USER_ID))).thenReturn(managedProfileUserInfo);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @MediumTest
     @Test
     public void testDontLogCancelledCall() {
diff --git a/tests/src/com/android/server/telecom/tests/CallRecordingTonePlayerTest.java b/tests/src/com/android/server/telecom/tests/CallRecordingTonePlayerTest.java
index eca374b..5151d4c 100644
--- a/tests/src/com/android/server/telecom/tests/CallRecordingTonePlayerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallRecordingTonePlayerTest.java
@@ -39,6 +39,7 @@
 import com.android.server.telecom.CallRecordingTonePlayer;
 import com.android.server.telecom.TelecomSystem;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -80,6 +81,12 @@
         when(mAudioManager.getActiveRecordingConfigurations()).thenReturn(null);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     /**
      * Ensures that child calls are not tracked.
      */
diff --git a/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java b/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java
index 169c56a..90d4dee 100644
--- a/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallRedirectionProcessorTest.java
@@ -39,6 +39,7 @@
 import com.android.server.telecom.callredirection.CallRedirectionProcessor;
 import com.android.server.telecom.callredirection.CallRedirectionProcessorHelper;
 
+import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -126,6 +127,12 @@
                 anyInt(), eq(UserHandle.CURRENT))).thenReturn(true);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     private void setIsInCarMode(boolean isInCarMode) {
         when(mSystemStateHelper.isCarMode()).thenReturn(isInCarMode);
     }
diff --git a/tests/src/com/android/server/telecom/tests/CallScreeningServiceControllerTest.java b/tests/src/com/android/server/telecom/tests/CallScreeningServiceControllerTest.java
index 1a01a95..24cc8d2 100644
--- a/tests/src/com/android/server/telecom/tests/CallScreeningServiceControllerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallScreeningServiceControllerTest.java
@@ -29,6 +29,7 @@
 import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.provider.CallLog;
+import android.provider.CallLog.Calls;
 import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -45,8 +46,10 @@
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.callfiltering.CallFilterResultCallback;
 import com.android.server.telecom.callfiltering.CallFilteringResult;
+import com.android.server.telecom.callfiltering.CallFilteringResult.Builder;
 import com.android.server.telecom.callfiltering.CallScreeningServiceController;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -69,31 +72,6 @@
 @RunWith(JUnit4.class)
 public class CallScreeningServiceControllerTest extends TelecomTestCase {
 
-    @Mock Context mContext;
-    @Mock Call mCall;
-    @Mock private CallFilterResultCallback mCallback;
-    @Mock CallsManager mCallsManager;
-    @Mock RoleManagerAdapter mRoleManagerAdapter;
-    @Mock CarrierConfigManager mCarrierConfigManager;
-    @Mock private TelecomManager mTelecomManager;
-    @Mock PackageManager mPackageManager;
-    @Mock ParcelableCallUtils.Converter mParcelableCallUtilsConverter;
-    @Mock PhoneAccountRegistrar mPhoneAccountRegistrar;
-    @Mock private CallerInfoLookupHelper mCallerInfoLookupHelper;
-
-    CallScreeningServiceHelper.AppLabelProxy mAppLabelProxy =
-            new CallScreeningServiceHelper.AppLabelProxy() {
-        @Override
-        public CharSequence getAppLabel(String packageName) {
-            return APP_NAME;
-        }
-    };
-
-    private ResolveInfo mResolveInfo;
-    private TelecomServiceImpl.SettingsSecureAdapter mSettingsSecureAdapter =
-            spy(new CallScreeningServiceFilterTest.SettingsSecureAdapterFake());
-    private TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() { };
-
     private static final String CALL_ID = "u89prgt9ps78y5";
     private static final Uri TEST_HANDLE = Uri.parse("tel:1235551234");
     private static final String DEFAULT_DIALER_PACKAGE = "com.android.dialer";
@@ -106,27 +84,46 @@
             "com.android.dialer", "com.android.dialer.callscreeningserviceimpl");
     private static final ComponentName USER_CHOSEN_CALL_SCREENING = new ComponentName(
             "com.android.userchosen", "com.android.userchosen.callscreeningserviceimpl");
-
-    private static final CallFilteringResult PASS_RESULT = new CallFilteringResult(
-            true, // shouldAllowCall
-            false, // shouldReject
-            true, // shouldAddToCallLog
-            true // shouldShowNotification
-    );
-
-    public static class SettingsSecureAdapterFake implements
-            TelecomServiceImpl.SettingsSecureAdapter {
-        @Override
-        public void putStringForUser(ContentResolver resolver, String name, String value,
-                                     int userHandle) {
-
-        }
-
-        @Override
-        public String getStringForUser(ContentResolver resolver, String name, int userHandle) {
-            return USER_CHOSEN_CALL_SCREENING.flattenToString();
-        }
-    }
+    private static final CallFilteringResult PASS_RESULT = new Builder()
+            .setShouldAllowCall(true)
+            .setShouldReject(false)
+            .setShouldAddToCallLog(true)
+            .setShouldShowNotification(true)
+            .build();
+    @Mock
+    Context mContext;
+    @Mock
+    Call mCall;
+    @Mock
+    CallsManager mCallsManager;
+    @Mock
+    RoleManagerAdapter mRoleManagerAdapter;
+    @Mock
+    CarrierConfigManager mCarrierConfigManager;
+    @Mock
+    PackageManager mPackageManager;
+    @Mock
+    ParcelableCallUtils.Converter mParcelableCallUtilsConverter;
+    @Mock
+    PhoneAccountRegistrar mPhoneAccountRegistrar;
+    CallScreeningServiceHelper.AppLabelProxy mAppLabelProxy =
+            new CallScreeningServiceHelper.AppLabelProxy() {
+                @Override
+                public CharSequence getAppLabel(String packageName) {
+                    return APP_NAME;
+                }
+            };
+    @Mock
+    private CallFilterResultCallback mCallback;
+    @Mock
+    private TelecomManager mTelecomManager;
+    @Mock
+    private CallerInfoLookupHelper mCallerInfoLookupHelper;
+    private ResolveInfo mResolveInfo;
+    private TelecomServiceImpl.SettingsSecureAdapter mSettingsSecureAdapter =
+            spy(new CallScreeningServiceFilterTest.SettingsSecureAdapterFake());
+    private TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() {
+    };
 
     @Override
     @Before
@@ -144,7 +141,7 @@
         when(TelecomManager.from(mContext)).thenReturn(mTelecomManager);
         when(mTelecomManager.getDefaultDialerPackage()).thenReturn(DEFAULT_DIALER_PACKAGE);
 
-        mResolveInfo =  new ResolveInfo() {{
+        mResolveInfo = new ResolveInfo() {{
             serviceInfo = new ServiceInfo();
             serviceInfo.packageName = PKG_NAME;
             serviceInfo.name = CLS_NAME;
@@ -160,6 +157,12 @@
         when(mCall.getHandle()).thenReturn(TEST_HANDLE);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testAllAllowCall() {
@@ -229,30 +232,27 @@
 
         controller.startFilterLookup(mCall, mCallback);
 
-        controller.onCallScreeningFilterComplete(mCall, new CallFilteringResult(
-                false, // shouldAllowCall
-                true, // shouldReject
-                false, // shouldAddToCallLog
-                true, // shouldShowNotification
-                CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE,
-                APP_NAME,
-                CARRIER_DEFINED_CALL_SCREENING.flattenToString()
-        ), CARRIER_DEFINED_CALL_SCREENING.getPackageName());
+        CallFilteringResult expectedResult = new Builder()
+                .setShouldAllowCall(false)
+                .setShouldReject(true)
+                .setShouldAddToCallLog(false)
+                .setShouldShowNotification(true)
+                .setCallBlockReason(Calls.BLOCK_REASON_CALL_SCREENING_SERVICE)
+                .setCallScreeningAppName(APP_NAME)
+                .setCallScreeningComponentName(
+                        CARRIER_DEFINED_CALL_SCREENING.flattenToString())
+                .build();
+
+        controller.onCallScreeningFilterComplete(mCall, expectedResult,
+                CARRIER_DEFINED_CALL_SCREENING.getPackageName());
 
         verify(mContext, times(1)).bindServiceAsUser(any(Intent.class),
                 any(ServiceConnection.class),
                 eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
                 eq(UserHandle.CURRENT));
 
-        verify(mCallback).onCallFilteringComplete(eq(mCall), eq(new CallFilteringResult(
-                false, // shouldAllowCall
-                true, // shouldReject
-                false, // shouldAddToCallLog
-                true, // shouldShowNotification
-                CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE, //callBlockReason
-                APP_NAME, //callScreeningAppName
-                CARRIER_DEFINED_CALL_SCREENING.flattenToString() //callScreeningComponentName
-        )));
+        verify(mCallback)
+                .onCallFilteringComplete(eq(mCall), eq(expectedResult));
     }
 
     @SmallTest
@@ -275,30 +275,31 @@
         callerInfo.contactExists = false;
         queryListener.onCallerInfoQueryComplete(TEST_HANDLE, callerInfo);
 
-        controller.onCallScreeningFilterComplete(mCall, new CallFilteringResult(
-                false, // shouldAllowCall
-                true, // shouldReject
-                false, // shouldAddToCallLog
-                true, // shouldShowNotification
-                CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE,
-                APP_NAME,
-                DEFAULT_DIALER_CALL_SCREENING.flattenToString()
-        ), DEFAULT_DIALER_CALL_SCREENING.getPackageName());
+        CallFilteringResult.Builder resultBuilder = new Builder()
+                .setShouldAllowCall(false)
+                .setShouldReject(true)
+                .setShouldShowNotification(true)
+                .setCallBlockReason(Calls.BLOCK_REASON_CALL_SCREENING_SERVICE)
+                .setCallScreeningAppName(APP_NAME)
+                .setCallScreeningComponentName(DEFAULT_DIALER_CALL_SCREENING.flattenToString());
+
+        CallFilteringResult providedResult = resultBuilder
+                .setShouldAddToCallLog(false)
+                .build();
+
+        controller.onCallScreeningFilterComplete(mCall, providedResult,
+                DEFAULT_DIALER_CALL_SCREENING.getPackageName());
 
         verify(mContext, times(3)).bindServiceAsUser(any(Intent.class),
                 any(ServiceConnection.class),
                 eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
                 eq(UserHandle.CURRENT));
 
-        verify(mCallback).onCallFilteringComplete(eq(mCall), eq(new CallFilteringResult(
-                false, // shouldAllowCall
-                true, // shouldReject
-                true, // shouldAddToCallLog (we don't allow services to skip call log)
-                true, // shouldShowNotification
-                CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE, //callBlockReason
-                APP_NAME, //callScreeningAppName
-                DEFAULT_DIALER_CALL_SCREENING.flattenToString() //callScreeningComponentName
-        )));
+        CallFilteringResult expectedResult = resultBuilder
+                .setShouldAddToCallLog(true)
+                .build();
+
+        verify(mCallback).onCallFilteringComplete(eq(mCall), eq(expectedResult));
     }
 
     @SmallTest
@@ -323,30 +324,99 @@
 
         controller.onCallScreeningFilterComplete(mCall, PASS_RESULT,
                 DEFAULT_DIALER_CALL_SCREENING.getPackageName());
-        controller.onCallScreeningFilterComplete(mCall, new CallFilteringResult(
-                false, // shouldAllowCall
-                true, // shouldReject
-                false, // shouldAddToCallLog
-                true, // shouldShowNotification
-                CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE,
-                APP_NAME,
-                USER_CHOSEN_CALL_SCREENING.flattenToString()
-        ), USER_CHOSEN_CALL_SCREENING.getPackageName());
+
+        CallFilteringResult.Builder resultBuilder = new Builder()
+                .setShouldAllowCall(false)
+                .setShouldReject(true)
+                .setShouldShowNotification(true)
+                .setCallBlockReason(Calls.BLOCK_REASON_CALL_SCREENING_SERVICE)
+                .setCallScreeningAppName(APP_NAME)
+                .setCallScreeningComponentName(DEFAULT_DIALER_CALL_SCREENING.flattenToString());
+        CallFilteringResult providedResult = resultBuilder
+                .setShouldAddToCallLog(false)
+                .build();
+
+        controller.onCallScreeningFilterComplete(mCall, providedResult,
+                USER_CHOSEN_CALL_SCREENING.getPackageName());
 
         verify(mContext, times(3)).bindServiceAsUser(any(Intent.class),
                 any(ServiceConnection.class),
                 eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
                 eq(UserHandle.CURRENT));
 
-        verify(mCallback).onCallFilteringComplete(eq(mCall), eq(new CallFilteringResult(
-                false, // shouldAllowCall
-                true, // shouldReject
-                true, // shouldAddToCallLog (we don't allow services to skip call log)
-                true, // shouldShowNotification
-                CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE, //callBlockReason
-                APP_NAME, //callScreeningAppName
-                USER_CHOSEN_CALL_SCREENING.flattenToString() //callScreeningComponentName
-        )));
+        CallFilteringResult expectedResult = resultBuilder
+                .setShouldAddToCallLog(true)
+                .build();
+
+        verify(mCallback).onCallFilteringComplete(eq(mCall), eq(expectedResult));
+    }
+
+    /**
+     * This test verifies that where the default dialer role is filled by the same app as the caller
+     * id and spam role, we will only bind to that call screening service once.
+     */
+    @SmallTest
+    @Test
+    public void testOnlyBindOnce() {
+        // Assume the user chose the default dialer to also fill the caller id and spam role.
+        when(mRoleManagerAdapter.getDefaultCallScreeningApp()).thenReturn(
+                DEFAULT_DIALER_CALL_SCREENING.getPackageName());
+        CallScreeningServiceController controller = new CallScreeningServiceController(mContext,
+                mCallsManager,
+                mPhoneAccountRegistrar, mParcelableCallUtilsConverter, mLock,
+                mSettingsSecureAdapter, mCallerInfoLookupHelper, mAppLabelProxy);
+
+        controller.startFilterLookup(mCall, mCallback);
+
+        controller.onCallScreeningFilterComplete(mCall, PASS_RESULT,
+                CARRIER_DEFINED_CALL_SCREENING.getPackageName());
+
+        CallerInfoLookupHelper.OnQueryCompleteListener queryListener = verifyLookupStart();
+        CallerInfo callerInfo = new CallerInfo();
+        callerInfo.contactExists = false;
+        queryListener.onCallerInfoQueryComplete(TEST_HANDLE, callerInfo);
+
+        controller.onCallScreeningFilterComplete(mCall, new CallFilteringResult.Builder()
+                        .setShouldAllowCall(false)
+                        .setShouldReject(true)
+                        .setShouldAddToCallLog(false)
+                        .setShouldShowNotification(true)
+                        .setCallBlockReason(CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE)
+                        .setCallScreeningAppName(APP_NAME)
+                        .setCallScreeningComponentName(
+                                DEFAULT_DIALER_CALL_SCREENING.flattenToString())
+                        .build(),
+                DEFAULT_DIALER_CALL_SCREENING.getPackageName());
+
+        controller.onCallScreeningFilterComplete(mCall, new CallFilteringResult.Builder()
+                .setShouldAllowCall(false)
+                .setShouldReject(true)
+                .setShouldAddToCallLog(false)
+                .setShouldShowNotification(true)
+                .setCallBlockReason(CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE)
+                .setCallScreeningAppName(APP_NAME)
+                .setCallScreeningComponentName(DEFAULT_DIALER_CALL_SCREENING.flattenToString())
+                .build(), USER_CHOSEN_CALL_SCREENING.getPackageName());
+
+        // Expect to bind twice; once to the carrier defined service, and then again to the default
+        // dialer.
+        verify(mContext, times(2)).bindServiceAsUser(any(Intent.class),
+                any(ServiceConnection.class),
+                eq(Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE),
+                eq(UserHandle.CURRENT));
+
+        // Expect filtering to complete only a single time from the default dialer service.
+        verify(mCallback, times(1)).onCallFilteringComplete(eq(mCall),
+                eq(new CallFilteringResult.Builder()
+                        .setShouldAllowCall(false)
+                        .setShouldReject(true)
+                        .setShouldAddToCallLog(true)
+                        .setShouldShowNotification(true)
+                        .setCallBlockReason(CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE)
+                        .setCallScreeningAppName(APP_NAME)
+                        .setCallScreeningComponentName(
+                                DEFAULT_DIALER_CALL_SCREENING.flattenToString())
+                        .build()));
     }
 
     private CallerInfoLookupHelper.OnQueryCompleteListener verifyLookupStart() {
@@ -370,4 +440,18 @@
                 .thenReturn(mCarrierConfigManager);
         when(mCarrierConfigManager.getConfig()).thenReturn(bundle);
     }
+
+    public static class SettingsSecureAdapterFake implements
+            TelecomServiceImpl.SettingsSecureAdapter {
+        @Override
+        public void putStringForUser(ContentResolver resolver, String name, String value,
+                int userHandle) {
+
+        }
+
+        @Override
+        public String getStringForUser(ContentResolver resolver, String name, int userHandle) {
+            return USER_CHOSEN_CALL_SCREENING.flattenToString();
+        }
+    }
 }
diff --git a/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java b/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
index 5b47fe9..7b3a499 100644
--- a/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallScreeningServiceFilterTest.java
@@ -29,7 +29,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.provider.CallLog;
+import android.provider.CallLog.Calls;
 import android.telecom.CallScreeningService;
 import android.telecom.ParcelableCall;
 import android.telecom.TelecomManager;
@@ -44,9 +44,11 @@
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.TelecomServiceImpl;
 import com.android.server.telecom.callfiltering.CallFilteringResult;
+import com.android.server.telecom.callfiltering.CallFilteringResult.Builder;
 import com.android.server.telecom.callfiltering.CallScreeningServiceFilter;
 import com.android.server.telecom.TelecomSystem;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -104,20 +106,20 @@
     private static final String USER_CHOSEN_CALL_SCREENING_APP_NAME = "UserChosen";
     private ResolveInfo mResolveInfo;
 
-    private static final CallFilteringResult PASS_RESULT = new CallFilteringResult(
-            true, // shouldAllowCall
-            false, // shouldReject
-            true, // shouldAddToCallLog
-            true // shouldShowNotification
-    );
+    private static final CallFilteringResult PASS_RESULT = new Builder()
+            .setShouldAllowCall(true)
+            .setShouldReject(false)
+            .setShouldAddToCallLog(true)
+            .setShouldShowNotification(true)
+            .build();
 
-    private static final CallFilteringResult PASS_RESULT_WITH_SILENCE = new CallFilteringResult(
-            true, // shouldAllowCall
-            false, // shouldReject
-            true, // shouldSilence
-            true, // shouldAddToCallLog
-            true // shouldShowNotification
-    );
+    private static final CallFilteringResult PASS_RESULT_WITH_SILENCE = new Builder()
+            .setShouldAllowCall(true)
+            .setShouldReject(false)
+            .setShouldSilence(true)
+            .setShouldAddToCallLog(true)
+            .setShouldShowNotification(true)
+            .build();
 
     private CallScreeningServiceFilter mFilter;
 
@@ -162,6 +164,12 @@
                 anyInt(), eq(UserHandle.CURRENT))).thenReturn(true);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testNoPackageName() {
@@ -267,15 +275,17 @@
                 true, // shouldShowNotification
                 CARRIER_DEFINED_CALL_SCREENING
         );
-        verify(mCallback).onCallScreeningFilterComplete(eq(mCall), eq(new CallFilteringResult(
-                false, // shouldAllowCall
-                true, // shouldReject
-                false, // shouldAddToCallLog
-                true, // shouldShowNotification
-                CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE, //callBlockReason
-                CARRIER_DEFINED_CALL_SCREENING_APP_NAME, //callScreeningAppName
-                CARRIER_DEFINED_CALL_SCREENING.flattenToString() //callScreeningComponentName
-        )), eq(CARRIER_DEFINED_CALL_SCREENING.getPackageName()));
+        verify(mCallback).onCallScreeningFilterComplete(eq(mCall), eq(new Builder()
+                        .setShouldAllowCall(false)
+                        .setShouldReject(true)
+                        .setShouldAddToCallLog(false)
+                        .setShouldShowNotification(true)
+                        .setCallBlockReason(Calls.BLOCK_REASON_CALL_SCREENING_SERVICE)
+                        .setCallScreeningAppName(CARRIER_DEFINED_CALL_SCREENING_APP_NAME)
+                        .setCallScreeningComponentName(
+                                CARRIER_DEFINED_CALL_SCREENING.flattenToString())
+                        .build()),
+                eq(CARRIER_DEFINED_CALL_SCREENING.getPackageName()));
     }
 
     @SmallTest
@@ -300,15 +310,16 @@
             true, // shouldShowNotification
             DEFAULT_DIALER_CALL_SCREENING
         );
-        verify(mCallback).onCallScreeningFilterComplete(eq(mCall), eq(new CallFilteringResult(
-            false, // shouldAllowCall
-            true, // shouldReject
-            true, // shouldAddToCallLog
-            true, // shouldShowNotification
-            CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE, //callBlockReason
-            DEFAULT_DIALER_APP_NAME, //callScreeningAppName
-            DEFAULT_DIALER_CALL_SCREENING.flattenToString() //callScreeningComponentName
-        )), eq(DEFAULT_DIALER_CALL_SCREENING.getPackageName()));
+        verify(mCallback).onCallScreeningFilterComplete(eq(mCall), eq(new Builder()
+                .setShouldAllowCall(false)
+                .setShouldReject(true)
+                .setShouldAddToCallLog(true)
+                .setShouldShowNotification(true)
+                .setCallBlockReason(Calls.BLOCK_REASON_CALL_SCREENING_SERVICE)
+                .setCallScreeningAppName(DEFAULT_DIALER_APP_NAME)
+                .setCallScreeningComponentName(DEFAULT_DIALER_CALL_SCREENING.flattenToString())
+                .build()),
+                eq(DEFAULT_DIALER_CALL_SCREENING.getPackageName()));
     }
 
     @SmallTest
@@ -333,15 +344,16 @@
             true, // shouldShowNotification
             USER_CHOSEN_CALL_SCREENING
         );
-        verify(mCallback).onCallScreeningFilterComplete(eq(mCall), eq(new CallFilteringResult(
-            false, // shouldAllowCall
-            true, // shouldReject
-            true, // shouldAddToCallLog
-            true, // shouldShowNotification
-            CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE, //callBlockReason
-            USER_CHOSEN_CALL_SCREENING_APP_NAME, //callScreeningAppName
-            USER_CHOSEN_CALL_SCREENING.flattenToString() //callScreeningComponentName
-        )), eq(USER_CHOSEN_CALL_SCREENING.getPackageName()));
+        verify(mCallback).onCallScreeningFilterComplete(eq(mCall), eq(new Builder()
+                        .setShouldAllowCall(false)
+                        .setShouldReject(true)
+                        .setShouldAddToCallLog(true)
+                        .setShouldShowNotification(true)
+                        .setCallBlockReason(Calls.BLOCK_REASON_CALL_SCREENING_SERVICE)
+                        .setCallScreeningAppName(USER_CHOSEN_CALL_SCREENING_APP_NAME)
+                        .setCallScreeningComponentName(USER_CHOSEN_CALL_SCREENING.flattenToString())
+                        .build()),
+                eq(USER_CHOSEN_CALL_SCREENING.getPackageName()));
     }
 
     private ServiceConnection verifyBindingIntent() {
diff --git a/tests/src/com/android/server/telecom/tests/CallerInfoLookupHelperTest.java b/tests/src/com/android/server/telecom/tests/CallerInfoLookupHelperTest.java
index e26d7e5..d4bff89 100644
--- a/tests/src/com/android/server/telecom/tests/CallerInfoLookupHelperTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallerInfoLookupHelperTest.java
@@ -44,6 +44,7 @@
 import com.android.server.telecom.ContactsAsyncHelper;
 import com.android.server.telecom.TelecomSystem;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -102,6 +103,12 @@
         }
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testLookupWithEmptyHandle() {
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index 7846fe9..fdd1d89 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -61,7 +61,6 @@
 import com.android.server.telecom.ClockProxy;
 import com.android.server.telecom.ConnectionServiceFocusManager;
 import com.android.server.telecom.ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory;
-import com.android.server.telecom.ConnectionServiceWrapper;
 import com.android.server.telecom.DefaultDialerCache;
 import com.android.server.telecom.EmergencyCallHelper;
 import com.android.server.telecom.HeadsetMediaButton;
@@ -83,7 +82,9 @@
 import com.android.server.telecom.WiredHeadsetManager;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
 import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
+import com.android.server.telecom.callfiltering.IncomingCallFilter;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -173,6 +174,8 @@
     @Mock private CallAudioModeStateMachine.Factory mCallAudioModeStateMachineFactory;
     @Mock private BluetoothStateReceiver mBluetoothStateReceiver;
     @Mock private RoleManagerAdapter mRoleManagerAdapter;
+    @Mock private IncomingCallFilter.Factory mIncomingCallFilterFactory;
+    @Mock private IncomingCallFilter mIncomingCallFilter;
 
     private CallsManager mCallsManager;
 
@@ -193,6 +196,8 @@
                 anyInt())).thenReturn(mCallAudioRouteStateMachine);
         when(mCallAudioModeStateMachineFactory.create(any(), any()))
                 .thenReturn(mCallAudioModeStateMachine);
+        when(mIncomingCallFilterFactory.create(any(), any(), any(), any(), any(), any()))
+                .thenReturn(mIncomingCallFilter);
         when(mClockProxy.currentTimeMillis()).thenReturn(System.currentTimeMillis());
         when(mClockProxy.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime());
         when(mConnSvrFocusManagerFactory.create(any())).thenReturn(mConnectionSvrFocusMgr);
@@ -222,7 +227,8 @@
                 mCallAudioRouteStateMachineFactory,
                 mCallAudioModeStateMachineFactory,
                 mInCallControllerFactory,
-                mRoleManagerAdapter);
+                mRoleManagerAdapter,
+                mIncomingCallFilterFactory);
 
         when(mPhoneAccountRegistrar.getPhoneAccount(
                 eq(SELF_MANAGED_HANDLE), any())).thenReturn(SELF_MANAGED_ACCOUNT);
@@ -232,6 +238,12 @@
                 eq(SIM_2_HANDLE), any())).thenReturn(SIM_2_ACCOUNT);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @MediumTest
     @Test
     public void testConstructPossiblePhoneAccounts() throws Exception {
@@ -251,7 +263,7 @@
 
         Call ongoingCall = new Call(
                 "1", /* callId */
-                mComponentContextFixture.getTestDouble(),
+                mContext,
                 mCallsManager,
                 mLock,
                 null /* ConnectionServiceRepository */,
@@ -1014,7 +1026,8 @@
     public void testIsInEmergencyCallLocal() {
         // Setup a call which is considered emergency based on its phone number.
         Call ongoingCall = addSpyCall();
-        when(mPhoneNumberUtilsAdapter.isLocalEmergencyNumber(any(), any())).thenReturn(true);
+        when(mComponentContextFixture.getTelephonyManager().isEmergencyNumber(any()))
+                .thenReturn(true);
         ongoingCall.setHandle(Uri.fromParts("tel", "5551212", null),
                 TelecomManager.PRESENTATION_ALLOWED);
 
@@ -1090,7 +1103,7 @@
 
     private Call addSpyCall(PhoneAccountHandle targetPhoneAccount) {
         Call ongoingCall = new Call(String.format("TC@%d", sCallId++), /* callId */
-                mComponentContextFixture.getTestDouble(),
+                mContext,
                 mCallsManager,
                 mLock, /* ConnectionServiceRepository */
                 null,
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index 86bbadb..e6e8ba1 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -51,6 +51,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IInterface;
+import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.telecom.CallAudioState;
@@ -209,6 +210,10 @@
                 return Context.AUDIO_SERVICE;
             } else if (svcClass == TelephonyManager.class) {
                 return Context.TELEPHONY_SERVICE;
+            } else if (svcClass == CarrierConfigManager.class) {
+                return Context.CARRIER_CONFIG_SERVICE;
+            } else if (svcClass == SubscriptionManager.class) {
+                return Context.TELEPHONY_SUBSCRIPTION_SERVICE;
             }
             throw new UnsupportedOperationException();
         }
@@ -513,6 +518,9 @@
 
         when(mNotificationManager.matchesCallFilter(any(Bundle.class))).thenReturn(true);
 
+        when(mCarrierConfigManager.getConfig()).thenReturn(new PersistableBundle());
+        when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(new PersistableBundle());
+
         when(mUserManager.getSerialNumberForUser(any(UserHandle.class))).thenReturn(-1L);
 
         doReturn(null).when(mApplicationContextSpy).registerReceiver(any(BroadcastReceiver.class),
diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
index 9655476..3718419 100644
--- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFixture.java
@@ -29,8 +29,10 @@
 import android.content.Context;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.IInterface;
+import android.os.Looper;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.telecom.CallAudioState;
@@ -658,6 +660,19 @@
         mExtrasLock = new CountDownLatch(1);
     }
 
+    public void waitForHandlerToClear() {
+        mConnectionServiceDelegate.getHandler().removeCallbacksAndMessages(null);
+        final CountDownLatch lock = new CountDownLatch(1);
+        mConnectionServiceDelegate.getHandler().post(lock::countDown);
+        while (lock.getCount() > 0) {
+            try {
+                lock.await(TelecomSystemTest.TEST_TIMEOUT, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                // do nothing
+            }
+        }
+    }
+
     private ParcelableConference parcelable(ConferenceInfo c) {
         return new ParcelableConference(
                 c.phoneAccount,
diff --git a/tests/src/com/android/server/telecom/tests/ConnectionServiceFocusManagerTest.java b/tests/src/com/android/server/telecom/tests/ConnectionServiceFocusManagerTest.java
index 77a9c0d..0d6ceba 100644
--- a/tests/src/com/android/server/telecom/tests/ConnectionServiceFocusManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/ConnectionServiceFocusManagerTest.java
@@ -23,6 +23,7 @@
 import com.android.server.telecom.ConnectionServiceFocusManager;
 import com.android.server.telecom.ConnectionServiceFocusManager.*;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -71,6 +72,12 @@
         mCallsManagerListener = captor.getValue();
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testRequestFocusWithoutActiveFocusExisted() {
diff --git a/tests/src/com/android/server/telecom/tests/ContactsAsyncHelperTest.java b/tests/src/com/android/server/telecom/tests/ContactsAsyncHelperTest.java
index 08d9a47..10cac93 100644
--- a/tests/src/com/android/server/telecom/tests/ContactsAsyncHelperTest.java
+++ b/tests/src/com/android/server/telecom/tests/ContactsAsyncHelperTest.java
@@ -32,12 +32,15 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import androidx.test.InstrumentationRegistry;
 
 import com.android.server.telecom.ContactsAsyncHelper;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -46,6 +49,7 @@
 
 import java.io.FileNotFoundException;
 import java.io.InputStream;
+import java.util.concurrent.Executor;
 
 @RunWith(JUnit4.class)
 public class ContactsAsyncHelperTest extends TelecomTestCase {
@@ -96,10 +100,17 @@
         mContext = InstrumentationRegistry.getTargetContext();
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testEmptyUri() throws Exception {
-        ContactsAsyncHelper cah = new ContactsAsyncHelper(mNullContentResolverAdapter);
+        ContactsAsyncHelper cah = new ContactsAsyncHelper(mNullContentResolverAdapter,
+                Looper.getMainLooper());
         try {
             cah.startObtainPhotoAsync(TOKEN, mContext, null, mListener, COOKIE);
         } catch (IllegalStateException e) {
@@ -113,7 +124,8 @@
     @SmallTest
     @Test
     public void testNullReturnFromOpenInputStream() {
-        ContactsAsyncHelper cah = new ContactsAsyncHelper(mNullContentResolverAdapter);
+        ContactsAsyncHelper cah = new ContactsAsyncHelper(mNullContentResolverAdapter,
+                Looper.getMainLooper());
         cah.startObtainPhotoAsync(TOKEN, mContext, SAMPLE_CONTACT_PHOTO_URI, mListener, COOKIE);
 
         verify(mListener, timeout(TEST_TIMEOUT)).onImageLoadComplete(eq(TOKEN),
@@ -123,7 +135,8 @@
     @SmallTest
     @Test
     public void testImageScaling() {
-        ContactsAsyncHelper cah = new ContactsAsyncHelper(mWorkingContentResolverAdapter);
+        ContactsAsyncHelper cah = new ContactsAsyncHelper(mWorkingContentResolverAdapter,
+                Looper.getMainLooper());
         cah.startObtainPhotoAsync(TOKEN, mContext, SAMPLE_CONTACT_PHOTO_URI, mListener, COOKIE);
 
         ArgumentCaptor<Drawable> photoCaptor = ArgumentCaptor.forClass(Drawable.class);
@@ -143,7 +156,8 @@
     @SmallTest
     @Test
     public void testNoScaling() {
-        ContactsAsyncHelper cah = new ContactsAsyncHelper(mWorkingContentResolverAdapter);
+        ContactsAsyncHelper cah = new ContactsAsyncHelper(mWorkingContentResolverAdapter,
+                Looper.getMainLooper());
         cah.startObtainPhotoAsync(TOKEN, mContext, SAMPLE_CONTACT_PHOTO_URI_SMALL,
                 mListener, COOKIE);
 
diff --git a/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
index c2db626..845a838 100644
--- a/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
+++ b/tests/src/com/android/server/telecom/tests/CreateConnectionProcessorTest.java
@@ -48,7 +48,9 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 
+import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
@@ -286,6 +288,45 @@
     }
 
     /**
+     * Ensure that when a test emergency number is being dialed and we restrict the usable
+     * PhoneAccounts using {@link PhoneAccountRegistrar#filterRestrictedPhoneAccounts(List)}, the
+     * test emergency call is sent on the filtered PhoneAccount.
+     */
+    @SmallTest
+    @Test
+    public void testFakeEmergencyNumber() throws Exception {
+        when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockCall.isTestEmergencyCall()).thenReturn(true);
+        // Put in a regular phone account as the target and make sure it calls that.
+        PhoneAccount regularAccount = makeEmergencyTestPhoneAccount("tel_acct1", 0);
+        PhoneAccountHandle regularAccountHandle = regularAccount.getAccountHandle();
+        List<PhoneAccount> filteredList = new ArrayList<>();
+        filteredList.add(regularAccount);
+        when(mMockAccountRegistrar.filterRestrictedPhoneAccounts(anyList()))
+                .thenReturn(filteredList);
+        mapToSubSlot(regularAccount, 1 /*subId*/, 0 /*slotId*/);
+        setTargetPhoneAccount(mMockCall, regularAccount.getAccountHandle());
+        phoneAccounts.add(regularAccount);
+        // Do not use this account, even though it is a SIM subscription and can place emergency
+        // calls
+        ConnectionServiceWrapper service = makeConnectionServiceWrapper();
+        PhoneAccount emergencyPhoneAccount = makeEmergencyPhoneAccount("tel_emer", 0);
+        mapToSubSlot(emergencyPhoneAccount, 2 /*subId*/, 1 /*slotId*/);
+        phoneAccounts.add(emergencyPhoneAccount);
+
+        mTestCreateConnectionProcessor.process();
+
+        verify(mMockCall).setConnectionManagerPhoneAccount(eq(regularAccountHandle));
+        verify(mMockCall).setTargetPhoneAccount(eq(regularAccountHandle));
+        verify(mMockCall).setConnectionService(eq(service));
+        verify(service).createConnection(eq(mMockCall), any(CreateConnectionResponse.class));
+        // Notify successful connection to call
+        CallIdMapper mockCallIdMapper = mock(CallIdMapper.class);
+        mTestCreateConnectionProcessor.handleCreateConnectionSuccess(mockCallIdMapper, null);
+        verify(mMockCreateConnectionResponse).handleCreateConnectionSuccess(mockCallIdMapper, null);
+    }
+
+    /**
      * Ensure that the non-emergency capable PhoneAccount and the SIM manager is not chosen to place
      * the emergency call if there is an emergency capable PhoneAccount available as well.
      */
@@ -293,6 +334,7 @@
     @Test
     public void testEmergencyCall() throws Exception {
         when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockCall.isTestEmergencyCall()).thenReturn(false);
         // Put in a regular phone account as the target to be sure it doesn't call that
         PhoneAccount regularAccount = makePhoneAccount("tel_acct1",
                 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
@@ -331,6 +373,7 @@
     @Test
     public void testEmergencyCallMultiSimNoPreferred() throws Exception {
         when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockCall.isTestEmergencyCall()).thenReturn(false);
         // Put in a non-SIM phone account as the target to be sure it doesn't call that.
         PhoneAccount regularAccount = makePhoneAccount("tel_acct1", 0);
         setTargetPhoneAccount(mMockCall, regularAccount.getAccountHandle());
@@ -368,6 +411,7 @@
     @Test
     public void testEmergencyCallMultiSimTelephonyPreferred() throws Exception {
         when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockCall.isTestEmergencyCall()).thenReturn(false);
         ConnectionServiceWrapper service = makeConnectionServiceWrapper();
         PhoneAccount emergencyPhoneAccount1 = makeEmergencyPhoneAccount("tel_emer1", 0);
         mapToSubSlot(emergencyPhoneAccount1, 1 /*subId*/, 0 /*slotId*/);
@@ -400,6 +444,7 @@
     @Test
     public void testEmergencyCallMultiSimUserPreferred() throws Exception {
         when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockCall.isTestEmergencyCall()).thenReturn(false);
         // Include a Connection Manager to be sure it doesn't call that
         PhoneAccount callManagerPA = createNewConnectionManagerPhoneAccountForCall(mMockCall,
                 "cm_acct", 0);
@@ -436,6 +481,7 @@
     @Test
     public void testEmergencyCallMultiSimUserPreferredInvalidSlot() throws Exception {
         when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockCall.isTestEmergencyCall()).thenReturn(false);
         // Include a Connection Manager to be sure it doesn't call that
         PhoneAccount callManagerPA = createNewConnectionManagerPhoneAccountForCall(mMockCall,
                 "cm_acct", 0);
@@ -472,6 +518,7 @@
     @Test
     public void testEmergencyCallMultiSimNoPreferenceInvalidSlot() throws Exception {
         when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockCall.isTestEmergencyCall()).thenReturn(false);
         // Include a Connection Manager to be sure it doesn't call that
         PhoneAccount callManagerPA = createNewConnectionManagerPhoneAccountForCall(mMockCall,
                 "cm_acct", 0);
@@ -503,6 +550,7 @@
     @Test
     public void testEmergencyCallSimFailToConnectionManager() throws Exception {
         when(mMockCall.isEmergencyCall()).thenReturn(true);
+        when(mMockCall.isTestEmergencyCall()).thenReturn(false);
         when(mMockCall.getHandle()).thenReturn(Uri.parse(""));
         // Put in a regular phone account to be sure it doesn't call that
         PhoneAccount regularAccount = makePhoneAccount("tel_acct1",
@@ -542,6 +590,16 @@
         verify(service).createConnection(eq(mMockCall), any(CreateConnectionResponse.class));
     }
 
+    private PhoneAccount makeEmergencyTestPhoneAccount(String id, int capabilities) {
+        final PhoneAccount emergencyPhoneAccount = makeQuickAccount(id, capabilities |
+                PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS);
+        PhoneAccountHandle emergencyPhoneAccountHandle = emergencyPhoneAccount.getAccountHandle();
+        givePhoneAccountBindPermission(emergencyPhoneAccountHandle);
+        when(mMockAccountRegistrar.getPhoneAccountUnchecked(emergencyPhoneAccountHandle))
+                .thenReturn(emergencyPhoneAccount);
+        return emergencyPhoneAccount;
+    }
+
     private PhoneAccount makeEmergencyPhoneAccount(String id, int capabilities) {
         final PhoneAccount emergencyPhoneAccount = makeQuickAccount(id, capabilities |
                 PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS |
diff --git a/tests/src/com/android/server/telecom/tests/DefaultDialerCacheTest.java b/tests/src/com/android/server/telecom/tests/DefaultDialerCacheTest.java
index e8bca03..e733465 100644
--- a/tests/src/com/android/server/telecom/tests/DefaultDialerCacheTest.java
+++ b/tests/src/com/android/server/telecom/tests/DefaultDialerCacheTest.java
@@ -30,6 +30,7 @@
 import com.android.server.telecom.RoleManagerAdapter;
 import com.android.server.telecom.TelecomSystem;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -101,6 +102,12 @@
         when(mRoleManagerAdapter.getDefaultDialerApp(eq(USER2))).thenReturn(DIALER3);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testThreeUsers() {
diff --git a/tests/src/com/android/server/telecom/tests/DirectToVoicemailCallFilterTest.java b/tests/src/com/android/server/telecom/tests/DirectToVoicemailCallFilterTest.java
index a685c5e..e62a9fc 100644
--- a/tests/src/com/android/server/telecom/tests/DirectToVoicemailCallFilterTest.java
+++ b/tests/src/com/android/server/telecom/tests/DirectToVoicemailCallFilterTest.java
@@ -17,16 +17,17 @@
 package com.android.server.telecom.tests;
 
 import android.net.Uri;
-import android.provider.CallLog;
+import android.provider.CallLog.Calls;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.telephony.CallerInfo;
 import com.android.server.telecom.Call;
 import com.android.server.telecom.callfiltering.CallFilterResultCallback;
 import com.android.server.telecom.CallerInfoLookupHelper;
-import com.android.server.telecom.callfiltering.CallFilteringResult;
+import com.android.server.telecom.callfiltering.CallFilteringResult.Builder;
 import com.android.server.telecom.callfiltering.DirectToVoicemailCallFilter;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,6 +53,12 @@
         super.setUp();
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testSendToVoicemail() {
@@ -61,16 +68,15 @@
         callerInfo.shouldSendToVoicemail = true;
 
         queryListener.onCallerInfoQueryComplete(TEST_HANDLE, callerInfo);
-        verify(mCallback).onCallFilteringComplete(mCall,
-                new CallFilteringResult(
-                        false, // shouldAllowCall
-                        true, // shouldReject
-                        true, // shouldAddToCallLog
-                        true, // shouldShowNotification
-                        CallLog.Calls.BLOCK_REASON_DIRECT_TO_VOICEMAIL, //callBlockReason
-                        null, //callScreeningAppName
-                        null // callScreeningComponentName
-                ));
+        verify(mCallback).onCallFilteringComplete(mCall, new Builder()
+                .setShouldAllowCall(false)
+                .setShouldReject(true)
+                .setShouldAddToCallLog(true)
+                .setShouldShowNotification(true)
+                .setCallBlockReason(Calls.BLOCK_REASON_DIRECT_TO_VOICEMAIL)
+                .setCallScreeningAppName(null)
+                .setCallScreeningComponentName(null)
+                .build());
     }
 
     @SmallTest
@@ -82,13 +88,12 @@
         callerInfo.shouldSendToVoicemail = false;
 
         queryListener.onCallerInfoQueryComplete(TEST_HANDLE, callerInfo);
-        verify(mCallback).onCallFilteringComplete(mCall,
-                new CallFilteringResult(
-                        true, // shouldAllowCall
-                        false, // shouldReject
-                        true, // shouldAddToCallLog
-                        true // shouldShowNotification
-                ));
+        verify(mCallback).onCallFilteringComplete(mCall, new Builder()
+                .setShouldAllowCall(true)
+                .setShouldReject(false)
+                .setShouldAddToCallLog(true)
+                .setShouldShowNotification(true)
+                .build());
     }
 
     @SmallTest
@@ -97,13 +102,12 @@
         CallerInfoLookupHelper.OnQueryCompleteListener queryListener = verifyLookupStart(null);
 
         queryListener.onCallerInfoQueryComplete(null, null);
-        verify(mCallback).onCallFilteringComplete(mCall,
-                new CallFilteringResult(
-                        true, // shouldAllowCall
-                        false, // shouldReject
-                        true, // shouldAddToCallLog
-                        true // shouldShowNotification
-                ));
+        verify(mCallback).onCallFilteringComplete(mCall, new Builder()
+                .setShouldAllowCall(true)
+                .setShouldReject(false)
+                .setShouldAddToCallLog(true)
+                .setShouldShowNotification(true)
+                .build());
     }
 
     private CallerInfoLookupHelper.OnQueryCompleteListener verifyLookupStart() {
diff --git a/tests/src/com/android/server/telecom/tests/DtmfLocalTonePlayerTest.java b/tests/src/com/android/server/telecom/tests/DtmfLocalTonePlayerTest.java
index 24dd18f..85a5278 100644
--- a/tests/src/com/android/server/telecom/tests/DtmfLocalTonePlayerTest.java
+++ b/tests/src/com/android/server/telecom/tests/DtmfLocalTonePlayerTest.java
@@ -22,6 +22,7 @@
 import com.android.server.telecom.DtmfLocalTonePlayer;
 import com.android.server.telecom.R;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -42,6 +43,7 @@
 
     DtmfLocalTonePlayer mPlayer;
 
+    @Override
     @Before
     public void setUp() throws Exception {
         super.setUp();
@@ -50,6 +52,12 @@
         when(mCall.getContext()).thenReturn(mContext);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testSupportedStart() {
diff --git a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
index bbefae3..2577631 100644
--- a/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
+++ b/tests/src/com/android/server/telecom/tests/InCallControllerTests.java
@@ -148,7 +148,10 @@
                 com.android.internal.R.string.config_defaultDialer);
         doReturn(SYS_CLASS).when(mMockResources).getString(R.string.incall_default_class);
         doReturn(true).when(mMockResources).getBoolean(R.bool.grant_location_permission_enabled);
-        mEmergencyCallHelper = new EmergencyCallHelper(mMockContext, SYS_PKG,
+        when(mDefaultDialerCache.getSystemDialerApplication()).thenReturn(SYS_PKG);
+        when(mDefaultDialerCache.getSystemDialerComponent()).thenReturn(
+                new ComponentName(SYS_PKG, SYS_CLASS));
+        mEmergencyCallHelper = new EmergencyCallHelper(mMockContext, mDefaultDialerCache,
                 mTimeoutsAdapter);
         when(mMockCallsManager.getRoleManagerAdapter()).thenReturn(mMockRoleManagerAdapter);
         mInCallController = new InCallController(mMockContext, mLock, mMockCallsManager,
@@ -166,6 +169,8 @@
     @Override
     @After
     public void tearDown() throws Exception {
+        mInCallController.getHandler().removeCallbacksAndMessages(null);
+        waitForHandlerAction(mInCallController.getHandler(), 1000);
         super.tearDown();
     }
 
diff --git a/tests/src/com/android/server/telecom/tests/IncomingCallFilterTest.java b/tests/src/com/android/server/telecom/tests/IncomingCallFilterTest.java
index e399088..8e2d11e 100644
--- a/tests/src/com/android/server/telecom/tests/IncomingCallFilterTest.java
+++ b/tests/src/com/android/server/telecom/tests/IncomingCallFilterTest.java
@@ -17,18 +17,21 @@
 package com.android.server.telecom.tests;
 
 import android.content.ContentResolver;
-import android.content.IContentProvider;
 import android.net.Uri;
-import android.provider.CallLog;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.CallLog.Calls;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.server.telecom.Call;
 import com.android.server.telecom.Timeouts;
 import com.android.server.telecom.callfiltering.CallFilterResultCallback;
 import com.android.server.telecom.callfiltering.CallFilteringResult;
+import com.android.server.telecom.callfiltering.CallFilteringResult.Builder;
 import com.android.server.telecom.callfiltering.IncomingCallFilter;
 import com.android.server.telecom.TelecomSystem;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -65,48 +68,50 @@
     private static final long SHORT_TIMEOUT = 100;
 
     private static final CallFilteringResult PASS_CALL_RESULT =
-            new CallFilteringResult(
-                    true, // shouldAllowCall
-                    false, // shouldReject
-                    true, // shouldAddToCallLog
-                    true // shouldShowNotification
-            );
+            new Builder()
+                    .setShouldAllowCall(true)
+                    .setShouldReject(false)
+                    .setShouldAddToCallLog(true)
+                    .setShouldShowNotification(true)
+                    .build();
 
     private static final CallFilteringResult ASYNC_BLOCK_CHECK_BLOCK_RESULT =
-            new CallFilteringResult(
-                    false, // shouldAllowCall
-                    true, // shouldReject
-                    true, // shouldAddToCallLog
-                    false, // shouldShowNotification
-                    CallLog.Calls.BLOCK_REASON_BLOCKED_NUMBER, //callBlockReason
-                    null, //callScreeningAppName
-                    null //callScreeningComponentName
-            );
+            new Builder()
+                    .setShouldAllowCall(false)
+                    .setShouldReject(true)
+                    .setShouldAddToCallLog(true)
+                    .setShouldShowNotification(false)
+                    .setCallBlockReason(Calls.BLOCK_REASON_BLOCKED_NUMBER)
+                    .setCallScreeningAppName(null)
+                    .setCallScreeningComponentName(null)
+                    .build();
 
     private static final CallFilteringResult DIRECT_TO_VOICEMAIL_CALL_BLOCK_RESULT =
-            new CallFilteringResult(
-                    false, // shouldAllowCall
-                    true, // shouldReject
-                    true, // shouldAddToCallLog
-                    true, // shouldShowNotification
-                    CallLog.Calls.BLOCK_REASON_DIRECT_TO_VOICEMAIL, //callBlockReason
-                    null, //callScreeningAppName
-                    null //callScreeningComponentName
-            );
+            new Builder()
+                    .setShouldAllowCall(false)
+                    .setShouldReject(true)
+                    .setShouldAddToCallLog(true)
+                    .setShouldShowNotification(true)
+                    .setCallBlockReason(Calls.BLOCK_REASON_DIRECT_TO_VOICEMAIL)
+                    .setCallScreeningAppName(null)
+                    .setCallScreeningComponentName(null)
+                    .build();
 
     private static final CallFilteringResult CALL_SCREENING_SERVICE_BLOCK_RESULT =
-            new CallFilteringResult(
-                    false, // shouldAllowCall
-                    true, // shouldReject
-                    false, // shouldAddToCallLog
-                    true, // shouldShowNotification
-                    CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE, //callBlockReason
-                    "com.android.thirdparty", //callScreeningAppName
-                    "com.android.thirdparty/com.android.thirdparty.callscreeningserviceimpl"
-                    //callScreeningComponentName
-            );
+            new Builder()
+                    .setShouldAllowCall(false)
+                    .setShouldReject(true)
+                    .setShouldAddToCallLog(false)
+                    .setShouldShowNotification(true)
+                    .setCallBlockReason(Calls.BLOCK_REASON_CALL_SCREENING_SERVICE)
+                    .setCallScreeningAppName("com.android.thirdparty")
+                    .setCallScreeningComponentName(
+                            "com.android.thirdparty/"
+                                    + "com.android.thirdparty.callscreeningserviceimpl")
+                    .build();
 
     private static final CallFilteringResult DEFAULT_RESULT = PASS_CALL_RESULT;
+    private Handler mHandler = new Handler(Looper.getMainLooper());
 
     @Override
     @Before
@@ -117,11 +122,19 @@
         setTimeoutLength(LONG_TIMEOUT);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        mHandler.removeCallbacksAndMessages(null);
+        waitForHandlerAction(mHandler, 1000);
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testAsyncBlockCallResultFilter() {
         IncomingCallFilter testFilter = new IncomingCallFilter(mContext, mResultCallback, mCall,
-                mLock, mTimeoutsAdapter, Collections.singletonList(mFilter1));
+                mLock, mTimeoutsAdapter, Collections.singletonList(mFilter1), mHandler);
         testFilter.performFiltering();
         verify(mFilter1).startFilterLookup(mCall, testFilter);
 
@@ -135,7 +148,7 @@
     @Test
     public void testDirectToVoiceMailCallResultFilter() {
         IncomingCallFilter testFilter = new IncomingCallFilter(mContext, mResultCallback, mCall,
-                mLock, mTimeoutsAdapter, Collections.singletonList(mFilter1));
+                mLock, mTimeoutsAdapter, Collections.singletonList(mFilter1), mHandler);
         testFilter.performFiltering();
         verify(mFilter1).startFilterLookup(mCall, testFilter);
 
@@ -149,7 +162,7 @@
     @Test
     public void testCallScreeningServiceBlockCallResultFilter() {
         IncomingCallFilter testFilter = new IncomingCallFilter(mContext, mResultCallback, mCall,
-                mLock, mTimeoutsAdapter, Collections.singletonList(mFilter1));
+                mLock, mTimeoutsAdapter, Collections.singletonList(mFilter1), mHandler);
         testFilter.performFiltering();
         verify(mFilter1).startFilterLookup(mCall, testFilter);
 
@@ -163,7 +176,7 @@
     @Test
     public void testPassCallResultFilter() {
         IncomingCallFilter testFilter = new IncomingCallFilter(mContext, mResultCallback, mCall,
-                mLock, mTimeoutsAdapter, Collections.singletonList(mFilter1));
+                mLock, mTimeoutsAdapter, Collections.singletonList(mFilter1), mHandler);
         testFilter.performFiltering();
         verify(mFilter1).startFilterLookup(mCall, testFilter);
 
@@ -183,7 +196,7 @@
                     add(mFilter4);
                 }};
         IncomingCallFilter testFilter = new IncomingCallFilter(mContext, mResultCallback, mCall,
-                mLock, mTimeoutsAdapter, filters);
+                mLock, mTimeoutsAdapter, filters, mHandler);
         testFilter.performFiltering();
         verify(mFilter1).startFilterLookup(mCall, testFilter);
         verify(mFilter2).startFilterLookup(mCall, testFilter);
@@ -195,16 +208,15 @@
         testFilter.onCallFilteringComplete(mCall, DIRECT_TO_VOICEMAIL_CALL_BLOCK_RESULT);
         testFilter.onCallFilteringComplete(mCall, CALL_SCREENING_SERVICE_BLOCK_RESULT);
         waitForHandlerAction(testFilter.getHandler(), SHORT_TIMEOUT * 2);
-        verify(mResultCallback).onCallFilteringComplete(eq(mCall), eq(
-                new CallFilteringResult(
-                        false, // shouldAllowCall
-                        true, // shouldReject
-                        false, // shouldAddToCallLog
-                        false, // shouldShowNotification
-                        CallLog.Calls.BLOCK_REASON_BLOCKED_NUMBER, //callBlockReason
-                        null, //callScreeningAppName
-                        null //callScreeningComponentName
-                )));
+        verify(mResultCallback).onCallFilteringComplete(eq(mCall), eq(new Builder()
+                .setShouldAllowCall(false)
+                .setShouldReject(true)
+                .setShouldAddToCallLog(false)
+                .setShouldShowNotification(false)
+                .setCallBlockReason(Calls.BLOCK_REASON_BLOCKED_NUMBER)
+                .setCallScreeningAppName(null)
+                .setCallScreeningComponentName(null)
+                .build()));
     }
 
     @SmallTest
@@ -217,7 +229,7 @@
                     add(mFilter3);
                 }};
         IncomingCallFilter testFilter = new IncomingCallFilter(mContext, mResultCallback, mCall,
-                mLock, mTimeoutsAdapter, filters);
+                mLock, mTimeoutsAdapter, filters, mHandler);
         testFilter.performFiltering();
         verify(mFilter1).startFilterLookup(mCall, testFilter);
         verify(mFilter2).startFilterLookup(mCall, testFilter);
@@ -227,16 +239,15 @@
         testFilter.onCallFilteringComplete(mCall, DIRECT_TO_VOICEMAIL_CALL_BLOCK_RESULT);
         testFilter.onCallFilteringComplete(mCall, CALL_SCREENING_SERVICE_BLOCK_RESULT);
         waitForHandlerAction(testFilter.getHandler(), SHORT_TIMEOUT * 2);
-        verify(mResultCallback).onCallFilteringComplete(eq(mCall), eq(
-                new CallFilteringResult(
-                        false, // shouldAllowCall
-                        true, // shouldReject
-                        false, // shouldAddToCallLog
-                        true, // shouldShowNotification
-                        CallLog.Calls.BLOCK_REASON_DIRECT_TO_VOICEMAIL, //callBlockReason
-                        null, ////callScreeningAppName
-                        null ////callScreeningComponentName
-                )));
+        verify(mResultCallback).onCallFilteringComplete(eq(mCall), eq(new Builder()
+                .setShouldAllowCall(false)
+                .setShouldReject(true)
+                .setShouldAddToCallLog(false)
+                .setShouldShowNotification(true)
+                .setCallBlockReason(Calls.BLOCK_REASON_DIRECT_TO_VOICEMAIL)
+                .setCallScreeningAppName(null)
+                .setCallScreeningComponentName(null)
+                .build()));
     }
 
     @SmallTest
@@ -248,7 +259,7 @@
                     add(mFilter2);
                 }};
         IncomingCallFilter testFilter = new IncomingCallFilter(mContext, mResultCallback, mCall,
-                mLock, mTimeoutsAdapter, filters);
+                mLock, mTimeoutsAdapter, filters, mHandler);
         testFilter.performFiltering();
         verify(mFilter1).startFilterLookup(mCall, testFilter);
         verify(mFilter2).startFilterLookup(mCall, testFilter);
@@ -256,17 +267,16 @@
         testFilter.onCallFilteringComplete(mCall, PASS_CALL_RESULT);
         testFilter.onCallFilteringComplete(mCall, CALL_SCREENING_SERVICE_BLOCK_RESULT);
         waitForHandlerAction(testFilter.getHandler(), SHORT_TIMEOUT * 2);
-        verify(mResultCallback).onCallFilteringComplete(eq(mCall), eq(
-                new CallFilteringResult(
-                        false, // shouldAllowCall
-                        true, // shouldReject
-                        false, // shouldAddToCallLog
-                        true, // shouldShowNotification
-                        CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE, //callBlockReason
-                        "com.android.thirdparty", //callScreeningAppName
-                        "com.android.thirdparty/com.android.thirdparty.callscreeningserviceimpl"
-                        //callScreeningComponentName
-                )));
+        verify(mResultCallback).onCallFilteringComplete(eq(mCall), eq(new Builder()
+                .setShouldAllowCall(false)
+                .setShouldReject(true)
+                .setShouldAddToCallLog(false)
+                .setShouldShowNotification(true)
+                .setCallBlockReason(Calls.BLOCK_REASON_CALL_SCREENING_SERVICE)
+                .setCallScreeningAppName("com.android.thirdparty")
+                .setCallScreeningComponentName(
+                        "com.android.thirdparty/com.android.thirdparty.callscreeningserviceimpl")
+                .build()));
     }
 
     @SmallTest
@@ -274,7 +284,7 @@
     public void testFilterTimeout() throws Exception {
         setTimeoutLength(SHORT_TIMEOUT);
         IncomingCallFilter testFilter = new IncomingCallFilter(mContext, mResultCallback, mCall,
-                mLock, mTimeoutsAdapter, Collections.singletonList(mFilter1));
+                mLock, mTimeoutsAdapter, Collections.singletonList(mFilter1), mHandler);
         testFilter.performFiltering();
         verify(mResultCallback, timeout((int) SHORT_TIMEOUT * 2)).onCallFilteringComplete(eq(mCall),
                 eq(DEFAULT_RESULT));
@@ -290,7 +300,7 @@
     public void testFilterTimeoutDoesntTrip() throws Exception {
         setTimeoutLength(SHORT_TIMEOUT);
         IncomingCallFilter testFilter = new IncomingCallFilter(mContext, mResultCallback, mCall,
-                mLock, mTimeoutsAdapter, Collections.singletonList(mFilter1));
+                mLock, mTimeoutsAdapter, Collections.singletonList(mFilter1), mHandler);
         testFilter.performFiltering();
         testFilter.onCallFilteringComplete(mCall, PASS_CALL_RESULT);
         waitForHandlerAction(testFilter.getHandler(), SHORT_TIMEOUT * 2);
diff --git a/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java b/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java
index f54783e..a871b73 100644
--- a/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java
+++ b/tests/src/com/android/server/telecom/tests/IncomingCallNotifierTest.java
@@ -28,6 +28,7 @@
 import com.android.server.telecom.HandoverState;
 import com.android.server.telecom.ui.IncomingCallNotifier;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -81,6 +82,12 @@
         when(mRingingCall.getHandoverState()).thenReturn(HandoverState.HANDOVER_NONE);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     /**
      * Add a call that isn't ringing.
      */
diff --git a/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
index a3bdb10..404fcbd 100644
--- a/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
@@ -187,6 +187,7 @@
     public void tearDown() throws Exception {
         TelecomSystem.setInstance(null);
         when(mTelecomSystem.isBootComplete()).thenReturn(false);
+        super.tearDown();
     }
 
     @SmallTest
diff --git a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
index 4ed1039..3438802 100644
--- a/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
+++ b/tests/src/com/android/server/telecom/tests/NewOutgoingCallIntentBroadcasterTest.java
@@ -38,7 +38,6 @@
 import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
@@ -54,6 +53,7 @@
 
 import com.android.server.telecom.Call;
 import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.DefaultDialerCache;
 import com.android.server.telecom.NewOutgoingCallIntentBroadcaster;
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.PhoneNumberUtilsAdapter;
@@ -62,6 +62,7 @@
 import com.android.server.telecom.SystemStateHelper;
 import com.android.server.telecom.TelecomSystem;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -88,15 +89,14 @@
     @Mock private PhoneAccount mPhoneAccount;
     @Mock private PhoneAccountRegistrar mPhoneAccountRegistrar;
     @Mock private RoleManagerAdapter mRoleManagerAdapter;
+    @Mock private DefaultDialerCache mDefaultDialerCache;
 
-    private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapterSpy;
+    private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter = new PhoneNumberUtilsAdapterImpl();
 
     @Override
     @Before
     public void setUp() throws Exception {
         super.setUp();
-        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
-        mPhoneNumberUtilsAdapterSpy = spy(new PhoneNumberUtilsAdapterImpl());
         when(mCall.getInitiatingUser()).thenReturn(UserHandle.CURRENT);
         when(mCallsManager.getLock()).thenReturn(new TelecomSystem.SyncRoot() { });
         when(mCallsManager.getSystemStateHelper()).thenReturn(mSystemStateHelper);
@@ -111,6 +111,12 @@
         when(mSystemStateHelper.isCarMode()).thenReturn(false);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testSelfManagedCall() {
@@ -210,8 +216,8 @@
     @Test
     public void testEmergencyCallWithNonDefaultDialer() {
         Uri handle = Uri.parse("tel:6505551911");
-        doReturn(true).when(mPhoneNumberUtilsAdapterSpy).isPotentialLocalEmergencyNumber(
-                any(Context.class), eq(handle.getSchemeSpecificPart()));
+        doReturn(true).when(mComponentContextFixture.getTelephonyManager())
+                .isPotentialEmergencyNumber(eq(handle.getSchemeSpecificPart()));
         Intent intent = new Intent(Intent.ACTION_CALL, handle);
 
         String ui_package_string = "sample_string_1";
@@ -220,6 +226,9 @@
                 ui_package_string);
         mComponentContextFixture.putResource(R.string.dialer_default_class,
                 dialer_default_class_string);
+        when(mDefaultDialerCache.getSystemDialerApplication()).thenReturn(ui_package_string);
+        when(mDefaultDialerCache.getSystemDialerComponent()).thenReturn(
+                new ComponentName(ui_package_string, dialer_default_class_string));
 
         int result = processIntent(intent, false).disconnectCause;
 
@@ -278,8 +287,8 @@
     @Test
     public void testActionEmergencyWithNonEmergencyNumber() {
         Uri handle = Uri.parse("tel:6505551911");
-        doReturn(false).when(mPhoneNumberUtilsAdapterSpy).isPotentialLocalEmergencyNumber(
-                any(Context.class), eq(handle.getSchemeSpecificPart()));
+        doReturn(false).when(mComponentContextFixture.getTelephonyManager())
+                .isPotentialEmergencyNumber(eq(handle.getSchemeSpecificPart()));
         Intent intent = new Intent(Intent.ACTION_CALL_EMERGENCY, handle);
         int result = processIntent(intent, true).disconnectCause;
 
@@ -292,8 +301,8 @@
         Uri handle = intent.getData();
         int videoState = VideoProfile.STATE_BIDIRECTIONAL;
         boolean isSpeakerphoneOn = true;
-        doReturn(true).when(mPhoneNumberUtilsAdapterSpy).isPotentialLocalEmergencyNumber(
-                any(Context.class), eq(handle.getSchemeSpecificPart()));
+        doReturn(true).when(mComponentContextFixture.getTelephonyManager())
+                .isPotentialEmergencyNumber(eq(handle.getSchemeSpecificPart()));
         intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, isSpeakerphoneOn);
         intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
 
@@ -414,8 +423,8 @@
         String newEmergencyNumber = "1234567890";
         result.receiver.setResultData(newEmergencyNumber);
 
-        doReturn(true).when(mPhoneNumberUtilsAdapterSpy).isPotentialLocalEmergencyNumber(
-                any(Context.class), eq(newEmergencyNumber));
+        doReturn(true).when(mComponentContextFixture.getTelephonyManager())
+                .isPotentialEmergencyNumber(eq(newEmergencyNumber));
         result.receiver.onReceive(mContext, result.intent);
         verify(mCall).disconnect(eq(0L));
     }
@@ -451,8 +460,8 @@
     private NewOutgoingCallIntentBroadcaster.CallDisposition processIntent(Intent intent,
             boolean isDefaultPhoneApp) {
         NewOutgoingCallIntentBroadcaster b = new NewOutgoingCallIntentBroadcaster(
-                mContext, mCallsManager, mCall, intent, mPhoneNumberUtilsAdapterSpy,
-                isDefaultPhoneApp);
+                mContext, mCallsManager, mCall, intent, mPhoneNumberUtilsAdapter,
+                isDefaultPhoneApp, mDefaultDialerCache);
         NewOutgoingCallIntentBroadcaster.CallDisposition cd = b.evaluateCall();
         if (cd.disconnectCause == DisconnectCause.NOT_DISCONNECTED) {
             b.processCall(cd);
diff --git a/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java b/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java
index 3f4a4cd..7104e3a 100644
--- a/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java
+++ b/tests/src/com/android/server/telecom/tests/ParcelableCallUtilsTest.java
@@ -32,6 +32,7 @@
 import com.android.server.telecom.PhoneNumberUtilsAdapter;
 import com.android.server.telecom.TelecomSystem;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -60,9 +61,10 @@
         when(mCallsManager.getCallerInfoLookupHelper()).thenReturn(mCallerInfoLookupHelper);
         when(mCallsManager.getPhoneAccountRegistrar()).thenReturn(mPhoneAccountRegistrar);
         when(mPhoneAccountRegistrar.getPhoneAccountUnchecked(any())).thenReturn(null);
-        when(mPhoneNumberUtilsAdapter.isLocalEmergencyNumber(any(), any())).thenReturn(false);
+        when(mComponentContextFixture.getTelephonyManager().isEmergencyNumber(any()))
+                .thenReturn(false);
         mCall = new Call("1",
-                null /* context */,
+                mContext /* context */,
                 mCallsManager,
                 mLock,
                 null /* ConnectionServiceRepository */,
@@ -78,6 +80,12 @@
                 mClockProxy /* ClockProxy */);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testParcelForNonSystemDialer() {
diff --git a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
index a978cfd..e9efacd 100644
--- a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
+++ b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
@@ -69,6 +69,7 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
@@ -153,6 +154,68 @@
         assertPhoneAccountEquals(input, result);
     }
 
+    @SmallTest
+    @Test
+    public void testFilterPhoneAccountForTest() throws Exception {
+        ComponentName componentA = new ComponentName("a", "a");
+        ComponentName componentB1 = new ComponentName("b", "b1");
+        ComponentName componentB2 = new ComponentName("b", "b2");
+        ComponentName componentC = new ComponentName("c", "c");
+
+        PhoneAccount simAccountA = new PhoneAccount.Builder(
+                makeQuickAccountHandle(componentA, "1"), "1")
+                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+                .setIsEnabled(true)
+                .build();
+
+        List<PhoneAccount> accountAList = new ArrayList<>();
+        accountAList.add(simAccountA);
+
+        PhoneAccount simAccountB1 = new PhoneAccount.Builder(
+                makeQuickAccountHandle(componentB1, "2"), "2")
+                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+                .setIsEnabled(true)
+                .build();
+
+        PhoneAccount simAccountB2 = new PhoneAccount.Builder(
+                makeQuickAccountHandle(componentB2, "3"), "3")
+                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+                .setIsEnabled(true)
+                .build();
+
+        List<PhoneAccount> accountBList = new ArrayList<>();
+        accountBList.add(simAccountB1);
+        accountBList.add(simAccountB2);
+
+        PhoneAccount simAccountC = new PhoneAccount.Builder(
+                makeQuickAccountHandle(componentC, "4"), "4")
+                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+                .setIsEnabled(true)
+                .build();
+
+        List<PhoneAccount> accountCList = new ArrayList<>();
+        accountCList.add(simAccountC);
+
+        List<PhoneAccount> allAccounts = new ArrayList<>();
+        allAccounts.addAll(accountAList);
+        allAccounts.addAll(accountBList);
+        allAccounts.addAll(accountCList);
+
+        assertEquals(allAccounts, mRegistrar.filterRestrictedPhoneAccounts(allAccounts));
+
+        mRegistrar.setTestPhoneAccountPackageNameFilter(componentA.getPackageName());
+        assertEquals(accountAList, mRegistrar.filterRestrictedPhoneAccounts(allAccounts));
+
+        mRegistrar.setTestPhoneAccountPackageNameFilter(componentB1.getPackageName());
+        assertEquals(accountBList, mRegistrar.filterRestrictedPhoneAccounts(allAccounts));
+
+        mRegistrar.setTestPhoneAccountPackageNameFilter(componentC.getPackageName());
+        assertEquals(accountCList, mRegistrar.filterRestrictedPhoneAccounts(allAccounts));
+
+        mRegistrar.setTestPhoneAccountPackageNameFilter(null);
+        assertEquals(allAccounts, mRegistrar.filterRestrictedPhoneAccounts(allAccounts));
+    }
+
     @MediumTest
     @Test
     public void testDefaultPhoneAccountHandleEmptyGroup() throws Exception {
diff --git a/tests/src/com/android/server/telecom/tests/RingerTest.java b/tests/src/com/android/server/telecom/tests/RingerTest.java
index 75e89bc..53ddd93 100644
--- a/tests/src/com/android/server/telecom/tests/RingerTest.java
+++ b/tests/src/com/android/server/telecom/tests/RingerTest.java
@@ -40,6 +40,7 @@
 import com.android.server.telecom.RingtoneFactory;
 import com.android.server.telecom.SystemSettingsUtil;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -147,6 +148,12 @@
         mRingerUnderTest.setBlockOnRingingFuture(mRingCompletionFuture);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testNoActionInTheaterMode() {
diff --git a/tests/src/com/android/server/telecom/tests/SessionManagerTest.java b/tests/src/com/android/server/telecom/tests/SessionManagerTest.java
index 85a9bff..cf84b7c 100644
--- a/tests/src/com/android/server/telecom/tests/SessionManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/SessionManagerTest.java
@@ -290,9 +290,9 @@
         assertTrue(childSession.isSessionCompleted());
         assertEquals(TEST_PARENT_NAME, mFullSessionMethodName);
         // Reduce flakiness by assuming that the true completion time is within a threshold of
-        // +-10 ms
-        assertTrue(mfullSessionCompleteTime >= TEST_DELAY_TIME - 10);
-        assertTrue(mfullSessionCompleteTime <= TEST_DELAY_TIME + 10);
+        // +-50 ms
+        assertTrue(mfullSessionCompleteTime >= TEST_DELAY_TIME / 2);
+        assertTrue(mfullSessionCompleteTime <= TEST_DELAY_TIME * 1.5);
     }
 
     /**
@@ -365,8 +365,8 @@
         mTestSessionManager.endSession();
 
         assertEquals(TEST_CHILD_NAME, mFullSessionMethodName);
-        assertTrue(mfullSessionCompleteTime >= TEST_DELAY_TIME - 10);
-        assertTrue(mfullSessionCompleteTime <= TEST_DELAY_TIME + 10);
+        assertTrue(mfullSessionCompleteTime >= TEST_DELAY_TIME / 2);
+        assertTrue(mfullSessionCompleteTime <= TEST_DELAY_TIME * 1.5);
     }
 
     /**
diff --git a/tests/src/com/android/server/telecom/tests/SessionTest.java b/tests/src/com/android/server/telecom/tests/SessionTest.java
index 7957a28..337041b 100644
--- a/tests/src/com/android/server/telecom/tests/SessionTest.java
+++ b/tests/src/com/android/server/telecom/tests/SessionTest.java
@@ -21,6 +21,8 @@
 import android.telecom.Logging.Session;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -30,7 +32,19 @@
  */
 
 @RunWith(JUnit4.class)
-public class SessionTest {
+public class SessionTest extends TelecomTestCase {
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
 
     /**
      * Ensure creating two sessions that are parent/child of each other does not lead to a crash
diff --git a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
index 185cb97..7d1bef7 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java
@@ -55,6 +55,7 @@
 import com.android.server.telecom.components.UserCallIntentProcessor;
 import com.android.server.telecom.components.UserCallIntentProcessorFactory;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -229,6 +230,12 @@
                 .thenReturn(true);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testGetDefaultOutgoingPhoneAccount() throws RemoteException {
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 82b17be..4fc9ed6 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -44,7 +44,6 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.IContentProvider;
 import android.content.Intent;
 import android.media.AudioManager;
 import android.media.IAudioService;
@@ -52,29 +51,33 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Process;
 import android.os.UserHandle;
-import android.provider.BlockedNumberContract;
 import android.telecom.Call;
 import android.telecom.ConnectionRequest;
 import android.telecom.DisconnectCause;
+import android.telecom.Log;
 import android.telecom.ParcelableCall;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
+import android.text.TextUtils;
 
 import com.android.internal.telecom.IInCallAdapter;
 import com.android.server.telecom.AsyncRingtonePlayer;
 import com.android.server.telecom.BluetoothPhoneServiceImpl;
 import com.android.server.telecom.CallAudioManager;
+import com.android.server.telecom.CallAudioModeStateMachine;
 import com.android.server.telecom.CallAudioRouteStateMachine;
 import com.android.server.telecom.CallerInfoLookupHelper;
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.CallsManagerListenerBase;
 import com.android.server.telecom.ClockProxy;
 import com.android.server.telecom.ConnectionServiceFocusManager;
+import com.android.server.telecom.ContactsAsyncHelper;
 import com.android.server.telecom.DefaultDialerCache;
 import com.android.server.telecom.HeadsetMediaButton;
 import com.android.server.telecom.HeadsetMediaButtonFactory;
@@ -88,13 +91,15 @@
 import com.android.server.telecom.ProximitySensorManagerFactory;
 import com.android.server.telecom.RoleManagerAdapter;
 import com.android.server.telecom.StatusBarNotifier;
+import com.android.server.telecom.SystemStateHelper;
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.Timeouts;
 import com.android.server.telecom.WiredHeadsetManager;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
+import com.android.server.telecom.callfiltering.CallFilterResultCallback;
+import com.android.server.telecom.callfiltering.IncomingCallFilter;
 import com.android.server.telecom.components.UserCallIntentProcessor;
 import com.android.server.telecom.ui.IncomingCallNotifier;
-import com.android.server.telecom.ui.MissedCallNotifierImpl.MissedCallNotifierImplFactory;
 
 import com.google.common.base.Predicate;
 
@@ -179,18 +184,6 @@
     }
 
     MissedCallNotifierFakeImpl mMissedCallNotifier = new MissedCallNotifierFakeImpl();
-    private class EmergencyNumberUtilsAdapter extends PhoneNumberUtilsAdapterImpl {
-
-        @Override
-        public boolean isLocalEmergencyNumber(Context context, String number) {
-            return mIsEmergencyCall;
-        }
-
-        @Override
-        public boolean isPotentialLocalEmergencyNumber(Context context, String number) {
-            return mIsEmergencyCall;
-        }
-    }
 
     private class IncomingCallAddedListener extends CallsManagerListenerBase {
 
@@ -206,8 +199,6 @@
         }
     }
 
-    PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter = new EmergencyNumberUtilsAdapter();
-
     @Mock HeadsetMediaButton mHeadsetMediaButton;
     @Mock ProximitySensorManager mProximitySensorManager;
     @Mock InCallWakeLockController mInCallWakeLockController;
@@ -216,6 +207,7 @@
     @Mock IncomingCallNotifier mIncomingCallNotifier;
     @Mock ClockProxy mClockProxy;
     @Mock RoleManagerAdapter mRoleManagerAdapter;
+    @Mock ToneGenerator mToneGenerator;
 
     final ComponentName mInCallServiceComponentNameX =
             new ComponentName(
@@ -333,9 +325,11 @@
 
     Context mSpyContext;
 
-    private int mNumOutgoingCallsMade;
+    ConnectionServiceFocusManager mConnectionServiceFocusManager;
 
-    private boolean mIsEmergencyCall;
+    private HandlerThread mHandlerThread;
+
+    private int mNumOutgoingCallsMade;
 
     class IdPair {
         final String mConnectionId;
@@ -354,9 +348,15 @@
         doReturn(mSpyContext).when(mSpyContext).getApplicationContext();
         doNothing().when(mSpyContext).sendBroadcastAsUser(any(), any(), any());
 
+        mHandlerThread = new HandlerThread("TelecomHandlerThread");
+        mHandlerThread.start();
+
         mNumOutgoingCallsMade = 0;
 
-        mIsEmergencyCall = false;
+        doReturn(false).when(mComponentContextFixture.getTelephonyManager())
+                .isEmergencyNumber(any());
+        doReturn(false).when(mComponentContextFixture.getTelephonyManager())
+                .isPotentialEmergencyNumber(any());
 
         // First set up information about the In-Call services in the mock Context, since
         // Telecom will search for these as soon as it is instantiated
@@ -364,6 +364,8 @@
 
         // Next, create the TelecomSystem, our system under test
         setupTelecomSystem();
+        // Need to reset teseting tag here
+        Log.setTag(TESTING_TAG);
 
         // Finally, register the ConnectionServices with the PhoneAccountRegistrar of the
         // now-running TelecomSystem
@@ -374,10 +376,30 @@
 
     @Override
     public void tearDown() throws Exception {
-        mTelecomSystem.getCallsManager().getCallAudioManager()
-                .getCallAudioRouteStateMachine().quitNow();
-        mTelecomSystem.getCallsManager().getCallAudioManager()
-                .getCallAudioModeStateMachine().quitNow();
+        mTelecomSystem.getCallsManager().waitOnHandlers();
+        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        waitForHandlerAction(mHandlerThread.getThreadHandler(), TEST_TIMEOUT);
+        // Bring down the threads that are active.
+        mHandlerThread.quit();
+        try {
+            mHandlerThread.join();
+        } catch (InterruptedException e) {
+            // don't do anything
+        }
+
+        mConnectionServiceFocusManager.getHandler().removeCallbacksAndMessages(null);
+        waitForHandlerAction(mConnectionServiceFocusManager.getHandler(), TEST_TIMEOUT);
+        mConnectionServiceFocusManager.getHandler().getLooper().quit();
+
+        mConnectionServiceFixtureA.waitForHandlerToClear();
+        mConnectionServiceFixtureB.waitForHandlerToClear();
+
+        // Print out any incomplete sessions for debugging tests
+        String sessions = Log.getSessionManager().printActiveSessions();
+        if (!TextUtils.isEmpty(sessions)) {
+            Log.w(this, "Active Sessions:\n" + sessions);
+        }
+
         mTelecomSystem = null;
         super.tearDown();
     }
@@ -430,6 +452,12 @@
 
         mCallerInfoAsyncQueryFactoryFixture = new CallerInfoAsyncQueryFactoryFixture();
 
+        ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory mConnServFMFactory =
+                requester -> {
+                    mConnectionServiceFocusManager = new ConnectionServiceFocusManager(requester);
+                    return mConnectionServiceFocusManager;
+                };
+
         mTimeoutsAdapter = mock(Timeouts.Adapter.class);
         when(mTimeoutsAdapter.getCallScreeningTimeoutMillis(any(ContentResolver.class)))
                 .thenReturn(TEST_TIMEOUT / 5L);
@@ -449,12 +477,12 @@
                 inCallWakeLockControllerFactory,
                 () -> mAudioService,
                 (context, lock, callsManager, phoneAccountRegistrar) -> mBluetoothPhoneServiceImpl,
-                ConnectionServiceFocusManager::new,
+                mConnServFMFactory,
                 mTimeoutsAdapter,
                 mAsyncRingtonePlayer,
-                mPhoneNumberUtilsAdapter,
+                new PhoneNumberUtilsAdapterImpl(),
                 mIncomingCallNotifier,
-                (streamType, volume) -> mock(ToneGenerator.class),
+                (streamType, volume) -> mToneGenerator,
                 new CallAudioRouteStateMachine.Factory() {
                     @Override
                     public CallAudioRouteStateMachine create(
@@ -472,11 +500,37 @@
                                 statusBarNotifier,
                                 audioServiceFactory,
                                 // Force enable an earpiece for the end-to-end tests
-                                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+                                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED,
+                                mHandlerThread.getLooper());
+                    }
+                },
+                new CallAudioModeStateMachine.Factory() {
+                    @Override
+                    public CallAudioModeStateMachine create(SystemStateHelper systemStateHelper,
+                            AudioManager am) {
+                        return new CallAudioModeStateMachine(systemStateHelper, am,
+                                mHandlerThread.getLooper());
                     }
                 },
                 mClockProxy,
-                mRoleManagerAdapter);
+                mRoleManagerAdapter,
+                new IncomingCallFilter.Factory() {
+                    @Override
+                    public IncomingCallFilter create(Context context,
+                            CallFilterResultCallback listener, com.android.server.telecom.Call call,
+                            TelecomSystem.SyncRoot lock, Timeouts.Adapter timeoutsAdapter,
+                            List<IncomingCallFilter.CallFilter> filters) {
+                        return new IncomingCallFilter(context, listener, call, lock,
+                                timeoutsAdapter, filters, mHandlerThread.getThreadHandler());
+                    }
+                },
+                new ContactsAsyncHelper.Factory() {
+                    @Override
+                    public ContactsAsyncHelper create(
+                            ContactsAsyncHelper.ContentResolverAdapter adapter) {
+                        return new ContactsAsyncHelper(adapter, mHandlerThread.getLooper());
+                    }
+                });
 
         mComponentContextFixture.setTelecomManager(new TelecomManager(
                 mComponentContextFixture.getTestDouble(),
@@ -646,7 +700,11 @@
         int startingNumConnections = connectionServiceFixture.mConnectionById.size();
         int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
 
-        mIsEmergencyCall = true;
+        doReturn(true).when(mComponentContextFixture.getTelephonyManager())
+                .isEmergencyNumber(any());
+        doReturn(true).when(mComponentContextFixture.getTelephonyManager())
+                .isPotentialEmergencyNumber(any());
+
         // Call will not use the ordered broadcaster, since it is an Emergency Call
         startOutgoingPhoneCallWaitForBroadcaster(number, phoneAccountHandle,
                 connectionServiceFixture, initiatingUser, videoState, true /*isEmergency*/);
diff --git a/tests/src/com/android/server/telecom/tests/TelecomTestCase.java b/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
index 637895b..b0b1ec0 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomTestCase.java
@@ -22,6 +22,7 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.concurrent.CountDownLatch;
@@ -36,6 +37,7 @@
 
     public void setUp() throws Exception {
         Log.setTag(TESTING_TAG);
+        Log.setIsExtendedLoggingEnabled(true);
         mMockitoHelper.setUp(InstrumentationRegistry.getContext(), getClass());
         mComponentContextFixture = new ComponentContextFixture();
         mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
@@ -47,6 +49,7 @@
     public void tearDown() throws Exception {
         mComponentContextFixture = null;
         mMockitoHelper.tearDown();
+        Mockito.framework().clearInlineMocks();
     }
 
     protected static void waitForHandlerAction(Handler h, long timeoutMillis) {
diff --git a/tests/src/com/android/server/telecom/tests/VideoProfileTest.java b/tests/src/com/android/server/telecom/tests/VideoProfileTest.java
index 6acadf7..5ee0414 100644
--- a/tests/src/com/android/server/telecom/tests/VideoProfileTest.java
+++ b/tests/src/com/android/server/telecom/tests/VideoProfileTest.java
@@ -23,6 +23,8 @@
 import android.telecom.VideoProfile;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -32,6 +34,19 @@
  */
 @RunWith(JUnit4.class)
 public class VideoProfileTest extends TelecomTestCase {
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     @SmallTest
     @Test
     public void testToString() {
diff --git a/tests/src/com/android/server/telecom/tests/VideoProviderProxyTest.java b/tests/src/com/android/server/telecom/tests/VideoProviderProxyTest.java
index b09aa5b..2b6c260 100644
--- a/tests/src/com/android/server/telecom/tests/VideoProviderProxyTest.java
+++ b/tests/src/com/android/server/telecom/tests/VideoProviderProxyTest.java
@@ -35,6 +35,7 @@
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.VideoProviderProxy;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -69,6 +70,12 @@
         mVideoProviderProxy.addListener(mListener);
     }
 
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
     /**
      * Tests the case where we receive a request to upgrade to video, except:
      * 1. Phone account says we support video.
diff --git a/tests/src/com/android/server/telecom/tests/VideoProviderTest.java b/tests/src/com/android/server/telecom/tests/VideoProviderTest.java
index eacecf9..597924d 100644
--- a/tests/src/com/android/server/telecom/tests/VideoProviderTest.java
+++ b/tests/src/com/android/server/telecom/tests/VideoProviderTest.java
@@ -34,8 +34,10 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
+import android.telecom.Call;
 import android.telecom.Connection;
 import android.telecom.Connection.VideoProvider;
+import android.telecom.DisconnectCause;
 import android.telecom.InCallService;
 import android.telecom.InCallService.VideoCall;
 import android.telecom.VideoCallImpl;
@@ -64,6 +66,8 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
+import com.android.server.telecom.CallsManager;
+
 /**
  * Performs tests of the {@link VideoProvider} and {@link VideoCall} APIs.  Ensures that requests
  * sent from an InCallService are routed through Telecom to a VideoProvider, and that callbacks are
@@ -116,13 +120,18 @@
         mVideoCallImpl = (VideoCallImpl) mVideoCall;
         mVideoCall.registerCallback(mVideoCallCallback);
 
+        // A little hacky, but we do not want CallsManager spawning InCallTonePlayer threads.
+        CallsManager callsManager = mTelecomSystem.getCallsManager();
+        callsManager.removeListener(callsManager.getCallAudioManager());
+
         mConnectionInfo = mConnectionServiceFixtureA.mConnectionById.get(mCallIds.mConnectionId);
         mVerificationLock = new CountDownLatch(1);
-        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
+        mTelecomSystem.getCallsManager().waitOnHandlers();
 
         doNothing().when(mContext).enforcePermission(anyString(), anyInt(), anyInt(), anyString());
         doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager).noteOp(anyInt(), anyInt(),
                 anyString());
+
     }
 
     @Override